heni-0.1.0.0: bench/campaign/Main.hs
module Main (main) where
import Control.Exception
import Data.Aeson
import Data.ByteString.Lazy.Char8 qualified as LBS
import Data.IORef
import Data.Version (showVersion)
import Definitions (exampleCampaign)
import GHC.Clock (getMonotonicTimeNSec)
import Paths_heni (getDataFileName)
import System.Directory qualified as Dir
import System.Environment (getArgs)
import System.FilePath (takeDirectory, (</>))
import System.Info qualified as Platform
import System.Posix.Temp (mkdtemp)
import Test.Heni
import Text.Read (readMaybe)
modify_ :: IORef a -> (a -> a) -> IO ()
modify_ ref f = atomicModifyIORef' ref (\x -> (f x, ()))
main :: IO ()
main = do
args <- getArgs
let parse [] = (1, "fresh")
parse ["--workers", n, "--mode", mode] | Just count <- readMaybe n, count > 0, count <= 256, mode `elem` ["fresh", "reuse"] = (count, mode)
parse _ = error "use --workers N --mode fresh|reuse"
(jobs, mode) = parse args
source <- getDataFileName "examples/minimal/heni-minimal-fixture.cabal" >>= pure . takeDirectory
bracket (Dir.getTemporaryDirectory >>= \p -> mkdtemp (p </> "heni-cabal-bench-")) Dir.removePathForcibly $ \root -> do
totals <- newIORef (0 :: Int, 0 :: Double, 0 :: Int, 0 :: Int)
let observe (CaseStarted _) = modify_ totals (\(baselines, buildTime, active, peak) -> (baselines, buildTime, active + 1, max peak (active + 1)))
observe (CaseFinished _) = modify_ totals (\(b, t, a, p) -> (b, t, a - 1, p))
observe (StageFinished _ role StageEvidence {stage = Build, durationSeconds}) = modify_ totals (\(b, t, a, p) -> (b + if role == "baseline" then 1 else 0, t + durationSeconds, a, p))
observe _ = pure ()
options = (defaultRunOptions source (root </> "run")) {workers = jobs, progress = observe, reusePolicy = if mode == "reuse" then ReuseBaselines "current GHC/Cabal and released fixture dependencies; no external state" Nothing else Fresh}
start <- getMonotonicTimeNSec
result <- runCampaign options exampleCampaign
end <- getMonotonicTimeNSec
(baselines, buildTime, _, peak) <- readIORef totals
LBS.putStrLn
( encode
( object
[ "workload" .= String "three real Cabal cases: caught, survivor, compiler error",
"mode" .= mode,
"workers" .= jobs,
"perBuildJobs" .= (1 :: Int),
"peakCaseConcurrency" .= peak,
"baselineBuildInvocations" .= baselines,
"elapsedSeconds" .= (fromIntegral (end - start) / 1e9 :: Double),
"sumBuildStageSeconds" .= buildTime,
"platform" .= object ["os" .= Platform.os, "arch" .= Platform.arch, "compiler" .= Platform.compilerName, "compilerVersion" .= showVersion Platform.compilerVersion],
"result" .= runResultJSON result
]
)
)