Generated Jogger Modules
At its core, PkgJogger
uses meta-programming to generate a Jogger module for running a package's benchmarks. For example, calling @jog
on Example
gives a jogger named JogExample
for running the benchmark suite of Example
:
julia> using PkgJogger, Example
julia> @jog Example
JogExample
Similarly, @jog AwesomePkg
would create a module named JogAwesomePkg
:
PkgJogger.@jog
— Macro@jog PkgName
Creates a module named JogPkgName
for running benchmarks for PkgName
.
Most edits to benchmark files are correctly tracked by Revise.jl. If they are not, re-run @jog PkgName
to fully reload JogPkgName
.
Revise must be loaded before calling
@jog PkgName
in order for edits to be automatically tracked.
Methods
suite
Return aBenchmarkGroup
of the benchmarks forPkgName
benchmark
Warmup, tune and run the suiterun
Dispatch toBenchmarkTools.run(suite(), args...; kwargs...)
save_benchmarks
Save benchmarks forPkgName
using an unique filename
Isolated Benchmarks
Each benchmark file, is wrapped in it's own module preventing code loaded in one file from being visible in another (unless explicitly included).
Example
using AwesomePkg, PkgJogger
@jog AwesomePkg
results = JogAwesomePkg.benchmark()
file = JogAwesomePkg.save_benchmarks(results)
Benchmark Directory Structure
PkgJogger
will recursively search the package's benchmark/
directory for benchmarking files bench_*.jl
or directories bench_*/
.
For example, the following directory:
.
+-- Project.toml
+-- src/
| +-- PkgName.j;
| ...
+-- benchmark
+-- bench_matrix.jl # Will be included
....
+-- subdir/ # ignored
+-- bench_ui/ # This gets added
+-- bench_foo.jl
....
Results in a benchmarking suite of:
1-element BenchmarkTools.BenchmarkGroup:
"bench_matrix.jl" => Suite from "benchmark/bench_matrix.jl"
... # Other benchmark/bench_*.jl files
"bench_ui" => BenchmarkTools.BenchmarkGroup:
"bench_foo.jl" => Suite from "benchmark/bench_ui/bench_foo.jl"
... # Other benchmark/bench_ui/bench_*.jl files
Benchmark Files
PkgJogger
expects the following structure for benchmarking files:
# PkgJogger will wrap this file into a module, thus it needs to declare all of
# it's `using` and `import` statements.
using BenchmarkTools
using OtherPkg
using AweseomePkg
# PkgJogger assumes the first `suite` variable is the benchmark suite for this file
suite = BenchmarkGroup()
# This will add a benchmark "foo" to the benchmarking suite with a key of:
# ["bench_filename.jl", "foo"]
suite["foo"] = @benchmarkable ...
# Further nesting within the file's `suite` is possible
s = suite["baz"] = BenchmarkGroup()
s["bar"] = @benchmarkable ... # Key of ["bench_filename.jl", "baz", "bar"]
In the example, we assume the benchmarking file is benchmark/bench_filename.jl
. If it was located in a subdirectory benchmark/bench_subdir
the resulting suite would have keys of ["bench_subdir", "bench_filename.jl", ...]
, instead of ["bench_filename.jl", ...]
. as shown.
A side effect of this structure is that each benchmarking file is self-contained and independent of other benchmarking files. This means that if you want to run the suite of a single file, you can
include
the file and run it with:tune!(suite); run(suite)
Jogger Reference
Jogger modules provide helper methods for working with their package's benchmarking suite. For reference, this section documents the methods for @jog Example
.
Main.JogExample.suite
— Functionsuite()::BenchmarkGroup
The BenchmarkTools suite for Example
Main.JogExample.benchmark
— Functionbenchmark(; verbose = false, save = false, ref = nothing)
Warmup, tune and run the benchmarking suite for Example.
If save = true
, will save the results using JogExample.save_benchmarks
and display the filename using @info
.
To reuse prior tuning results set ref
to a BenchmarkGroup or suitable identifier for JogExample.load_benchmarks
. See PkgJogger.tune!
for more information about re-using tuning results.
Main.JogExample.run
— Functionrun(args...; verbose::Bool = false, kwargs)
Run the benchmarking suite for Example. See BenchmarkTools.run
for more options
Main.JogExample.save_benchmarks
— Functionsave_benchmarks(results::BenchmarkGroup)::String
Saves benchmarking results for Example to BENCHMARK_DIR/trial/uuid4().bson.gz
, and returns the path to the saved results
Meta Data such as cpu load, time stamp, etc. are collected on save, not during benchmarking. For representative metadata, results should be saved immediately after benchmarking.
Results can be loaded with PkgJogger.load_benchmarks
or JogExample.load_benchmarks
Example
Running a benchmark suite and then saving the results
r = JogExample.benchmark()
filename = JogExample.save_benchmarks(r)
Equivalently:
JogExample.benchmark(; save = true)
Main.JogExample.load_benchmarks
— Functionload_benchmarks(id)::Dict
Loads benchmarking results for Example from BENCHMARK_DIR/trial
based on id
. The following are supported id
types:
- `filename::String`: Loads results from `filename`
- `uuid::Union{String, UUID}`: Loads results with the given UUID
- `:latest` loads the latest (By mtime) results from `BENCHMARK_DIR/trial`
- `:oldest` loads the oldest (By mtime) results from `BENCHMARK_DIR/trial`
Main.JogExample.judge
— Functionjudge(new, old; metric=Statistics.median, kwargs...)
Compares benchmarking results from new
vs old
for regressions/improvements using metric
as a basis. Additional kwargs
are passed to BenchmarkTools.judge
Identical to PkgJogger.judge
, but accepts any identifier supported by JogExample.load_benchmarks
Examples
# Judge the latest results vs. the oldest
JogExample.judge(:latest, :oldest)
[...]
# Judge results by UUID
JogExample.judge("0465dd67-1490-4b8a-b0ea-14478f811b63", "3e1e6960-1d62-4d2f-aee8-968a12177362")
[...]
# Judge using the minimum, instead of the median, time
JogExample.judge("path/to/results.bson.gz", "3df49659-00a1-4741-b259-7f45759b91c7"; metric=minimum)
[...]
Main.JogExample.BENCHMARK_DIR
— ConstantBENCHMARK_DIR
Directory of benchmarks for Example