blob: 7732ce9abf8f8a5fe1b93e218b0161300decc8b9 [file] [log] [blame] [view] [edit]
### bent
Bent automates downloading, compiling, and running Go tests and benchmarks from various Github repositories.
The benchmarks and tests retrieved are pinned at a particular version, which avoids that source of variation
and also avoids the problem of running unknown code.
Older versions of bent defaulted to running benchmarks in a container, however that is not the default now and it is no longer well tested.
Installation:
```
go install golang.org/x/benchmarks/cmd/bent@latest
```
Depends on burntsushi/toml.
Initial usage :
```
go install golang.org/x/benchmarks/cmd/bent@latest
mkdir scratch
cd scratch
bent -I
cp configurations-sample.toml configurations.toml
nano configurations.toml # or use your favorite editor
bent -v -N 10 # will run default set of ~50 benchmarks 10 times using supplied configuration(s)
```
The output binaries are placed in subdirectory `testbin`, various
benchmark results (from building, run, and others requested) are
placed in subdirectory `bench`, and the binaries are also incorporated
into Docker containers if Docker is used. Each benchmark and
configuration has a shortname, and the generated binaries combine
these shortnames, for example `gonum_mat_Tip` and `gonum_mat_Go1.9`.
Benchmark files are prefixed with a run timestamp, and grouped by
configuration, with various suffixes for the various benchmarks.
Run benchmarks appears in files with suffix `.stdout`.
Others are more obviously named, with suffixes `.build`, `.benchsize`, and `.benchdwarf`.
Flags for your use:
| Flag | meaning | example |
| --- | --- | --- |
| -v | print commands as they are run | |
| -I | initialize a directory for running tests | |
| -N x | benchmark/test repeat count | -N 25 |
| -a N | repeat builds for build benchmarking | -a 10 |
| -R N | for randomized builds, build a new binary<br>for each run (incompatible with -a, -N) | -R 3 |
| -B file | benchmarks file | -B benchmarks-trial.toml |
| -C file | configurations file | -C conf_1.9_and_tip.toml |
| -T | run tests instead of benchmarks | |
| -b list | run benchmarks in comma-separated list <br> (even if normally "disabled" )| -b uuid,gonum_topo |
| -c list | use configurations from comma-separated list <br> (even if normally "disabled") | -c Tip,Go1.9 |
| -l | list available benchmarks and configurations,<br>then exit | |
| | Less useful flags | |
| -r string | skip get and build, just run.<br>string names Docker image if needed,<br>if not using Docker any non-empty will do. | -r f10cecc3eaac |
| -s k | (build) shuffle flag, k = 0,1,2,3.<br>Randomize build order to reduce<br>sensitivity to other machine load | -s 2 |
| -G t/f | group runs by benchmark to reduce<br>time-of-day background noise (default false) | |
| -X | do not reset go.mod<br>for experiments involving modifications<br>to build/\*/go.mod | |
| -g | get benchmarks, but do not build or run | |
| -W | print benchmark information as a markdown table | |
| -sandbox | require Docker sandbox to run tests/benchmarks<br>(and exclude those that do not sandbox) | |
### Suite, Benchmark and Configuration files
Suites, benchmarks and configurations appear in toml format, since that is
somewhat more human-friendly than JSON and in particular allows comments.
Values in these files can refer to environment variables; `PATH`, `USER`, `HOME`, `SHELL`,
`BENT*`, and `GO*`. Unless it is explicitly supplied, `ROOT` is defined to be `$PWD`.
For iterated builds and benchmarks `BENT_I` is set to the iteration count, and
for runs `BENT_BINARY` is the file name (excluding path).
The Suite file defines the shortnames for benchmarks, where they are obtained,
and what version, plus any always-necessary flags:
```
[[Suites]]
Name = "gonum_mat"
Repo = "gonum.org/v1/gonum/mat"
BuildFlags = ["-tags", "safe"]
Version = "@v0.9.3"
[[Suites]]
Name = "toml"
Repo = "github.com/BurntSushi/toml"
Version = "@v1.3.2"
ExtraFiles = ["_example"]
```
Here, `gonum_mat` is checked out at version `0.9.3` and is always build with `-tags safe`.
And to run the toml benchmarks, the contents of the `_example` directory are also required.
A sample benchmark entry:
```
[[Benchmarks]]
Name = "gonum_topo"
Tests = "Test"
Benchmarks = "Benchmark(TarjanSCCGnp_1000_half|TarjanSCCGnp_10_tenth)"
# NotSandboxed = true # uncomment if cannot be run in a Docker container
# Disabled = true # uncomment to disable benchmark
```
Here, `Name` is a short name, and `Tests` and `Benchmarks` are the
regular expressions for `go test` specifying which tests or benchmarks to run.
A sample configuration entry with all the options supplied:
```
[[Configurations]]
Name = "Go-preempt"
Root = "$HOME/work/go/"
# Optional flags below
BuildFlags = ["-gccgoflags=all=-O3 -static-libgo","-tags=noasm"] # for Gollvm
AfterBuild = ["benchsize", "benchdwarf"]
GcFlags = "-d=ssa/insert_resched_checks/on"
LdFlags = "-randlayout=$BENT_I"
GcEnv = ["GOMAXPROCS=1","GOGC=200"]
RunFlags = ["-test.short"]
RunEnv = ["GOGC=1000"]
RunWrapper = ["cpuprofile"]
Disabled = false
```
The `Gc...` attributes apply to the test or benchmark compilation, the `Run...` attributes apply to the test or benchmark run.
A `RunWrapper` command receives the entire command line as arguments, plus the environment variable `BENT_BINARY` set to the filename
(excluding path) of the binary being run (for example, "uuid_Tip") and `BENT_I` set to the run number for this binary.
One useful example is `cpuprofile`:
```
#!/bin/bash
# Run args as command, but run cpuprofile and then pprof to capture test cpuprofile output
if [ -n ${BENT_PGO} ] ; then
pf="${BENT_PGO}/${BENT_BENCH}.prof"
mkdir -p ${BENT_PGO}
else
pf="${BENT_PROFILES}/${BENT_BINARY}_${BENT_I}.prof"
mkdir -p ${BENT_PROFILES}
fi
"$@" -test.cpuprofile="$pf"
echo cpuprofile in "$pf"
if [[ x`which pprof` == x"" ]] ; then
go tool pprof -text -flat -nodecount=20 "$pf"
else
pprof -text -flat -nodecount=20 "$pf"
fi
```
When both configuration and benchmark wrappers are used the configuration wrapper runs the benchmark wrapper runs the actual benchmark, i.e.
```
ConfigWrapper ConfigArg BenchWrapper BenchArg ActualBenchmark
```
The `Disabled` attribute for both benchmarks and configurations removes them from normal use,
but leaves them accessible to explicit request with `-b` or `-c`.
### Special configurations
Bent includes sample configurations to support PGO-optimized benchmarks and randomized link order to normalize away branch alignment artifacts. These may need editing to reference local paths before use.
For PGO optimization:
```
# First generate profiles
bent -C configurations-pgo.toml -c pgo-generate
# Use profiles for 10 benchmark runs
bent -C configurations-pgo.toml -N 10
```
For randomized link order:
```
# Randomized benchmarking -R is incompatible with -a or -N
bent -C configurations-random.toml -R 10
```
### Scripts
Bent now comes with several shell scripts to automate common uses.
These all run using [`perflock`](https://github.com/aclements/perflock) if it is available, and default to different
numbers of builds (usually 1) and benchmark runs (usually 15) which can be
overridden at invocation.<br>
#### `cmpcl.sh refs/changes/<nn>/<cl>/<patch> [options]`
This checks out a particular version of a CL, and its immediate predecessor, and benchmarks the change.
The `refs/changes/<nn>/<cl>/<patch>` parameter is the same one that appears as a Gerrit download option for the CL.
The default is to build once, benchmark 15 times. The results are also uploaded with [`benchsave`](https://github.com/golang/perf/tree/master/cmd/benchsave) to perf.golang.org.
#### `cmpjob.sh <branch-or-tag> <branch-or-tag> [options]`
This checks out two particular tag or branches, and benchmarks the difference.
This can be helpful when binary-searching a performance regression.
The default is to build once, benchmark 15 times. The results are also uploaded with [`benchsave`](https://github.com/golang/perf/tree/master/cmd/benchsave) to perf.golang.org.
#### `cronjob.sh [options]`
This checks out the current development tip and the most recent release (e.g. 1.14) and benchmarks
their difference. This can be helpful for nightly performance monitoring.
The default is to build 25 times and benchmark 25 times.
The results are also uploaded with [`benchsave`](https://github.com/golang/perf/tree/master/cmd/benchsave) to perf.golang.org.
The script also contains glue to tweet the results, but by default this will silently do nothing.
#### `cmpcl-phase.sh refs/changes/<nn>/<cl>/<patch> [options]`
This checks out a particular version of a CL, and its immediate predecessor,
compiles each once with the ssa phase timing flag turned on, does not run benchmarks,
and feeds the log (with all the embedded phase timings) to [phase-times](https://github.com/dr2chase/gc-phase-times)
to help spot any bad performance trends in the new CL.
The resulting CSVs can be [imported into a spreadsheet and graphed](https://docs.google.com/spreadsheets/d/1f1rTX73ett6iKMb5LuNpnG78T7CLucQAHRKBZuI23Q4/edit?usp=sharing)
(select the "Test" sheet and scroll down below the vast table of numbers, there is a pretty chart).