Tune a project
The cache can't help the first build after a real edit: every crate the edit reaches has to compile. What helps there is settings: a faster linker, less debug info, a parallel front end. shippy-bench tune measures them on real edits in your project and writes the winner as a shippy.toml for you to apply, so no project file changes.
What it found on tempo
Rebuild after an edit, geometric mean of three edits:
The linker alone is worth 1.4x, the stable set 1.5x, nightly 1.9x.
shippy.toml
shippy.toml at the workspace root holds settings shippy adds to every cargo command. This is tempo's, from bench/examples/tempo.shippy.toml:
[cargo]
toolchain = "nightly"
config = [
'target.x86_64-unknown-linux-gnu.linker="clang"',
'target.x86_64-unknown-linux-gnu.rustflags=["-Clink-arg=--ld-path=wild", "-Zthreads=8"]',
'profile.dev.debug=false',
]
[cache]
build_scripts = true # rocksdb, jemalloc and the other C builds are restored too
fixed_paths = true # every checkout shares, wherever it lives (needs bubblewrap)
[env]
SOURCE_DATE_EPOCH = "1794500000"Each config entry becomes a --config argument to cargo. It needs wild and clang on PATH. For stable Rust, drop toolchain and -Zthreads=8.
Find your own settings
Run it on one edit
From your checkout, name the file an edit goes to and the cargo command you run after it:
shippy-bench tune --file crates/foo/src/lib.rs --prune -- build -p foo --bin fooIt times plain cargo, then every setting stable Rust has (linker, debug info, split debug info, codegen units), and prints the winner as a shippy.toml. That is the whole of it for most projects. The next two steps are for several edits, nightly settings or a baseline of your own.
Describe a few edits
A bench.toml names your checkout and the edits to time. Each scenario edits one file and always puts it back, even on Ctrl-C.
repo = "/path/to/checkout"
runs = 5
[[scenario]]
name = "edit-leaf"
cargo = ["build", "-p", "foo", "--bin", "foo"]
file = "crates/foo/src/lib.rs"
edit = "append_fn" # adds a function, so dependents rebuild
[[scenario]]
name = "edit-body"
cargo = ["build", "-p", "foo", "--bin", "foo"]
file = "crates/bar/src/lib.rs"
edit = "replace"
find = "let limit = 10;"
replace = "let limit = 10 + ({n} - {n});" # {n} is new every time
[[variant]]
name = "cargo" # the baselineRun the tuner
shippy-bench tune --config bench.toml --knobs bench/knobs.default.toml --pruneWithout --knobs it tries what stable Rust has; knobs.default.toml adds the nightly settings. It starts from plain cargo, then tries each knob (linker, debug info, Cranelift, parallel front end, codegen units...) and keeps a value when it lowers the score by more than --min-gain (3% by default). --prune deletes the target directory of every candidate that lost.
Apply the winner
The tuner prints the winning settings and writes them to shippy.toml in its output directory. Copy that file to the workspace root, or add its [cargo] table to the shippy.toml there. shippy never applies them by itself: they change what is built (no debug info means no source lines in a debugger), and the first build with them compiles everything once.
Other settings that pay
- Keep the store and target directories in memory, or on a filesystem with reflinks. On ext4 a restored library is a link to the store's file, and a crate's metadata, executables and build scripts' output directories are copied out of the store: about half of what a build of tempo restores.
- Set
SOURCE_DATE_EPOCHif build scripts stamp the build time. On tempo it is the difference between 38 s and 6 s for every new checkout. - Leave linked outputs cached (the default) so undoing an edit restores the executable too.