packages feed

packed-data-0.1.0.2: benchmark/report/Criterion.hs

module Criterion where

import Data.Char (isDigit)
import Data.List (isInfixOf, isPrefixOf, isSuffixOf)
import Data.Scientific
import Debug.Trace (trace)
import System.Directory (removeFile)
import System.Process
import Text.Read (readMaybe)
import Types

runCriterionBenchmarks :: BenchSuite -> IO BenchmarkResults
runCriterionBenchmarks s = do
    (_, _, _, h) <- createProcess (shell criterionCmd)
    _ <- waitForProcess h
    csv <- readFile reportPath
    res <- parseCSV s csv
    removeFile reportPath
    return res
  where
    reportPath = "report.csv"
    criterionTestSuiteName = suiteToCriterionBenchName s
    criterionCmd =
        "cabal bench tree-bench --benchmark-options='--csv "
            ++ reportPath
            ++ " "
            ++ criterionTestSuiteName
            ++ "'"

suiteToCriterionBenchName :: BenchSuite -> String
suiteToCriterionBenchName = \case
    AST -> "ast"
    Sum -> "sum"
    RightMost -> "traversals/right-most"
    Incr -> "increment"

parseCSV :: BenchSuite -> String -> IO BenchmarkResults
parseCSV s csv = do
    let cleanCSV = drop 1 $ lines csv
    res <- parseCSVForCase cleanCSV s `mapM` cases
    return $ filter (not . null . values) res

parseCSVForCase :: [String] -> BenchSuite -> (String, String, Maybe String) -> IO BenchmarkResult
parseCSVForCase csvLines s (caseId, lang, subtitle) = do
    let caseLines = filter (('/' : caseId ++ ",") `isInfixOf`) csvLines
    caseValues <- parseCSVLineForCase `mapM` caseLines
    return $ BenchmarkResult s lang subtitle caseValues

parseCSVLineForCase :: (MonadFail m) => String -> m (Int, Double)
parseCSVLineForCase line =
    let res = do
            treeSize <- readMaybe rawTreeSize
            time <- readMaybe rawTime :: Maybe Scientific
            return (treeSize, toRealFloat time)
     in trace (show res) $ maybe (fail "Parsing error") return res
  where
    rawTime =
        takeWhile (/= ',') $
            drop 1 $
                dropWhile (/= ',') line
    rawTreeSize =
        takeWhile isDigit $
            dropWhile (not . isDigit) line

cases :: [(String, String, Maybe String)]
cases =
    [ ("c-inplace", "C", Just "Inplace")
    , ("c-new-tree", "C", Just "Produces new tree")
    , ("native", "Haskell", Nothing)
    , ("packed", "packed-data", Just "W/o Indirections")
    , ("packed-with-size", "packed-data", Just "W/ Indirections")
    , ("packed-unpacked", "packed-data", Just "Unpacking, then traverse")
    , ("non-monadic-packed", "packed-data", Just "Non-monadic, w/o Indirections")
    , ("non-monadic-packed-with-size", "packed-data", Just "Non-monadic, w/ Indirections")
    , ("packed-needsbuilder", "packed-data", Just "Using NeedsBuilder")
    , ("unpack-repack", "packed-data", Just "Unpacking, increment and repack")
    , ("packed-rebuild-repack", "packed-data", Just "Deserialise and increment, and repack")
    ]