Skip to content
This repository was archived by the owner on Jan 2, 2021. It is now read-only.

Commit 0d7cae9

Browse files
authored
Improve hist benchmarks driver and add to CI (#770)
* Remove hardcoded --stack-yaml and upstream/master assumption * support Cabal in bench suite * add benchmark run to CI Even if the time measurements are unreliable in a shared CI environment, the memory usage will be an accurate indicator of space leaks * Update bench/README * use origin/master * default to stack in benchmarks (for CI) * ignore ghcide-bench and ghcide-preprocessor binaries too * Review feedbacks * Add the v0.3.0 tag in bench/hist.yaml commented out to keep the CI time as tight as possible * Add .artifactignore file to avoid publishing binaries in azure bench pipeline * use default stack.yaml
1 parent ed95e69 commit 0d7cae9

File tree

8 files changed

+123
-32
lines changed

8 files changed

+123
-32
lines changed

.azure/linux-bench.yml

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
jobs:
2+
- job: ghcide_bench_linux
3+
timeoutInMinutes: 60
4+
pool:
5+
vmImage: 'ubuntu-latest'
6+
strategy:
7+
matrix:
8+
stack:
9+
STACK_YAML: "stack.yaml"
10+
steps:
11+
- checkout: self
12+
- task: Cache@2
13+
inputs:
14+
key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/$(STACK_YAML) | $(Build.SourcesDirectory)/ghcide.cabal
15+
path: .azure-cache
16+
cacheHitVar: CACHE_RESTORED
17+
displayName: "Cache stack artifacts"
18+
- bash: |
19+
mkdir -p ~/.stack
20+
tar xzf .azure-cache/stack-root.tar.gz -C $HOME
21+
displayName: "Unpack cache"
22+
condition: eq(variables.CACHE_RESTORED, 'true')
23+
- bash: |
24+
sudo add-apt-repository ppa:hvr/ghc
25+
sudo apt-get update
26+
sudo apt-get install -y g++ gcc libc6-dev libffi-dev libgmp-dev zlib1g-dev
27+
if ! which stack >/dev/null 2>&1; then
28+
curl -sSL https://get.haskellstack.org/ | sh
29+
fi
30+
displayName: 'Install Stack'
31+
- bash: stack setup --stack-yaml=$STACK_YAML
32+
displayName: 'stack setup'
33+
- bash: stack build --bench --only-dependencies --stack-yaml=$STACK_YAML
34+
displayName: 'stack build --only-dependencies'
35+
- bash: |
36+
export PATH=/opt/cabal/bin:$PATH
37+
stack bench --ghc-options=-Werror --stack-yaml=$STACK_YAML
38+
displayName: 'stack bench --ghc-options=-Werror'
39+
- bash: |
40+
mkdir -p .azure-cache
41+
tar czf .azure-cache/stack-root.tar.gz -C $HOME .stack
42+
displayName: "Pack cache"
43+
- bash: |
44+
cat bench-hist/results.csv
45+
displayName: "cat results"
46+
- publish: bench-hist
47+
artifact: benchmarks
48+
displayName: "publish"

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ bench-hist/
1212
bench-temp/
1313
.shake/
1414
ghcide
15+
ghcide-bench
16+
ghcide-preprocessor
1517
*.benchmark-gcStats
1618
tags

README.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -329,15 +329,17 @@ This writes a log file called `.tasty-rerun-log` of the failures, and only runs
329329
See the [tasty-rerun](https://hackage.haskell.org/package/tasty-rerun-1.1.17/docs/Test-Tasty-Ingredients-Rerun.html) documentation for other options.
330330

331331
If you are touching performance sensitive code, take the time to run a differential
332-
benchmark between HEAD and upstream using the benchHist script. The configuration in
333-
`bench/hist.yaml` is setup to do this by default assuming upstream is
334-
`origin/master`. Run the benchmarks with `stack`:
332+
benchmark between HEAD and master using the benchHist script. This assumes that
333+
"master" points to the upstream master.
334+
335+
Run the benchmarks with `stack`:
335336

336337
export STACK_YAML=...
337338
stack bench
338339

339-
It should take around 15 minutes and the results will be stored in the `bench-hist` folder.
340-
To interpret the results, see the comments in the `bench/hist/Main.hs` module.
340+
It should take around 15 minutes and the results will be stored in the `bench-hist` folder. To interpret the results, see the comments in the `bench/hist/Main.hs` module.
341+
342+
More details in [bench/README](bench/README.md)
341343

342344
### Building the extension
343345

azure-pipelines.yml

+1
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ pr:
1616
jobs:
1717
- template: ./.azure/linux-stack.yml
1818
- template: ./.azure/windows-stack.yml
19+
- template: ./.azure/linux-bench.yml

bench-hist/.artifactignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ghcide
2+
ghcide-bench
3+
ghcide-preprocessor
4+
*.benchmark-gcStats

bench/README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
This folder contains two Haskell programs that work together to simplify the
55
performance analysis of ghcide:
66

7-
- `exe/Main.hs` - a standalone benchmark suite. Run with `stack bench`
7+
- `exe/Main.hs` - a standalone benchmark runner. Run with `stack run ghcide-bench`
88
- `hist/Main.hs` - a Shake script for running the benchmark suite over a set of commits.
9-
- Run with `stack exec benchHist`,
10-
- Requires a `ghcide-bench` binary in the PATH,
11-
- Calls `stack` internally to build the project,
12-
- Driven by the `hist.yaml` configuration file. By default it compares HEAD with upstream
9+
- Run with `stack bench` or `cabal bench`,
10+
- Requires a `ghcide-bench` binary in the PATH (usually provided by stack/cabal),
11+
- Calls `cabal` (or `stack`, configurable) internally to build the project,
12+
- Driven by the `hist.yaml` configuration file.
13+
By default it compares HEAD with "master"
1314

1415
Further details available in the module header comments.

bench/hist.yaml

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# At least 100 is recommended in order to observe space leaks
33
samples: 100
44

5+
buildTool: stack
6+
57
# Path to the ghcide-bench binary to use for experiments
68
ghcideBench: ghcide-bench
79

@@ -37,6 +39,6 @@ versions:
3739
# - v0.0.6
3840
# - v0.1.0
3941
# - v0.2.0
40-
- upstream: upstream/master
42+
# - v0.3.0
43+
- upstream: origin/master
4144
- HEAD
42-

bench/hist/Main.hs

+51-20
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,14 @@
2525
For diff graphs, the "previous version" is the preceding entry in the list of versions
2626
in the config file. A possible improvement is to obtain this info via `git rev-list`.
2727
28-
The script relies on stack for building and running all the binaries.
29-
3028
To execute the script:
3129
32-
> stack bench
30+
> cabal/stack bench
3331
3432
To build a specific analysis, enumerate the desired file artifacts
3533
3634
> stack bench --ba "bench-hist/HEAD/results.csv bench-hist/HEAD/edit.diff.svg"
35+
> cabal bench --benchmark-options "bench-hist/HEAD/results.csv bench-hist/HEAD/edit.diff.svg"
3736
3837
-}
3938
{-# LANGUAGE DeriveAnyClass #-}
@@ -42,6 +41,7 @@
4241

4342
import Control.Applicative (Alternative (empty))
4443
import Control.Monad (when, forM, forM_, replicateM)
44+
import Data.Char (toLower)
4545
import Data.Foldable (find)
4646
import Data.Maybe (fromMaybe)
4747
import Data.Text (Text)
@@ -103,8 +103,10 @@ main = shakeArgs shakeOptions {shakeChange = ChangeModtimeAndDigest} $ do
103103
readSamples = askOracle $ GetSamples ()
104104
getParent = askOracle . GetParent
105105

106-
build <- liftIO $ outputFolder <$> readConfigIO config
106+
configStatic <- liftIO $ readConfigIO config
107107
ghcideBenchPath <- ghcideBench <$> liftIO (readConfigIO config)
108+
let build = outputFolder configStatic
109+
buildSystem = buildTool configStatic
108110

109111
phony "all" $ do
110112
Config {..} <- readConfig config
@@ -139,11 +141,8 @@ main = shakeArgs shakeOptions {shakeChange = ChangeModtimeAndDigest} $ do
139141
&%> \[out, ghcpath] -> do
140142
liftIO $ createDirectoryIfMissing True $ dropFileName out
141143
need =<< getDirectoryFiles "." ["src//*.hs", "exe//*.hs", "ghcide.cabal"]
142-
cmd_
143-
( "stack --local-bin-path=" <> takeDirectory out
144-
<> " --stack-yaml=stack88.yaml build ghcide:ghcide --copy-bins --ghc-options -rtsopts"
145-
)
146-
Stdout ghcLoc <- cmd (s "stack --stack-yaml=stack88.yaml exec which ghc")
144+
cmd_ $ buildGhcide buildSystem (takeDirectory out)
145+
ghcLoc <- findGhc buildSystem
147146
writeFile' ghcpath ghcLoc
148147

149148
[ build -/- "*/ghcide",
@@ -155,13 +154,8 @@ main = shakeArgs shakeOptions {shakeChange = ChangeModtimeAndDigest} $ do
155154
commitid <- readFile' $ b </> ver </> "commitid"
156155
cmd_ $ "git worktree add bench-temp " ++ commitid
157156
flip actionFinally (cmd_ (s "git worktree remove bench-temp --force")) $ do
158-
Stdout ghcLoc <- cmd [Cwd "bench-temp"] (s "stack --stack-yaml=stack88.yaml exec which ghc")
159-
cmd_
160-
[Cwd "bench-temp"]
161-
( "stack --local-bin-path=../"
162-
<> takeDirectory out
163-
<> " --stack-yaml=stack88.yaml build ghcide:ghcide --copy-bins --ghc-options -rtsopts"
164-
)
157+
ghcLoc <- findGhc buildSystem
158+
cmd_ [Cwd "bench-temp"] $ buildGhcide buildSystem (".." </> takeDirectory out)
165159
writeFile' ghcpath ghcLoc
166160

167161
priority 8000 $
@@ -198,7 +192,7 @@ main = shakeArgs shakeOptions {shakeChange = ChangeModtimeAndDigest} $ do
198192
RemEnv "GHC_PACKAGE_PATH",
199193
AddPath [takeDirectory ghcPath, "."] []
200194
]
201-
ghcideBenchPath
195+
ghcideBenchPath $
202196
[ "--timeout=3000",
203197
"-v",
204198
"--samples=" <> show samples,
@@ -208,7 +202,8 @@ main = shakeArgs shakeOptions {shakeChange = ChangeModtimeAndDigest} $ do
208202
"--ghcide=" <> ghcide,
209203
"--select",
210204
unescaped (unescapeExperiment (Escaped $ dropExtension exp))
211-
]
205+
] ++
206+
[ "--stack" | Stack == buildSystem]
212207
cmd_ Shell $ "mv *.benchmark-gcStats " <> dropFileName outcsv
213208

214209
build -/- "results.csv" %> \out -> do
@@ -259,7 +254,30 @@ main = shakeArgs shakeOptions {shakeChange = ChangeModtimeAndDigest} $ do
259254
title = show (unescapeExperiment exp) <> " - live bytes over time"
260255
plotDiagram False diagram out
261256

262-
----------------------------------------------------------------------------------------------------
257+
--------------------------------------------------------------------------------
258+
259+
buildGhcide :: BuildSystem -> String -> String
260+
buildGhcide Cabal out = unwords
261+
["cabal install"
262+
,"exe:ghcide"
263+
,"--installdir=" ++ out
264+
,"--install-method=copy"
265+
,"--overwrite-policy=always"
266+
,"--ghc-options -rtsopts"
267+
]
268+
buildGhcide Stack out =
269+
"stack --local-bin-path=" <> out
270+
<> " build ghcide:ghcide --copy-bins --ghc-options -rtsopts"
271+
272+
273+
findGhc :: BuildSystem -> Action FilePath
274+
findGhc Cabal =
275+
liftIO $ fromMaybe (error "ghc is not in the PATH") <$> findExecutable "ghc"
276+
findGhc Stack = do
277+
Stdout ghcLoc <- cmd (s "stack exec which ghc")
278+
return ghcLoc
279+
280+
--------------------------------------------------------------------------------
263281

264282
data Config = Config
265283
{ experiments :: [Unescaped String],
@@ -268,7 +286,8 @@ data Config = Config
268286
-- | Path to the ghcide-bench binary for the experiments
269287
ghcideBench :: FilePath,
270288
-- | Output folder ('foo' works, 'foo/bar' does not)
271-
outputFolder :: String
289+
outputFolder :: String,
290+
buildTool :: BuildSystem
272291
}
273292
deriving (Generic, Show)
274293
deriving anyclass (FromJSON, ToJSON)
@@ -312,6 +331,18 @@ findPrev name (x : y : xx)
312331
| otherwise = findPrev name (y : xx)
313332
findPrev name _ = name
314333

334+
data BuildSystem = Cabal | Stack
335+
deriving (Eq, Read, Show)
336+
337+
instance FromJSON BuildSystem where
338+
parseJSON x = fromString . map toLower <$> parseJSON x
339+
where
340+
fromString "stack" = Stack
341+
fromString "cabal" = Cabal
342+
fromString other = error $ "Unknown build system: " <> other
343+
344+
instance ToJSON BuildSystem where
345+
toJSON = toJSON . show
315346
----------------------------------------------------------------------------------------------------
316347

317348
-- | A line in the output of -S

0 commit comments

Comments
 (0)