Fast Builds

nearcore is implemented in Rust and is a fairly sizable project, so it takes a while to build. This chapter collects various tips to make the development process faster.

Optimizing build times is a bit of a black art, so please do benchmarks on your machine to verify that the improvements work for you. Changing some configuration and making a typo, which prevents it from improving build times is an extremely common failure mode!

Rust Perf Book contains a section on compilation times as well!

cargo build --release is obviously slower than cargo build. We enable full lto (link-time optimization), so our -r builds are very slow, use a lot of RAM, and don't utilize the available parallelism fully.

As debug builds are much too slow at runtime for many purposes, we have a custom profile --profile dev-release which is equivalent to -r, except that the time-consuming options such as LTO are disabled, and debug assertions are enabled.

Use --profile dev-release for most local development, or when connecting a locally built node to a network. Use -r for production, or if you want to get absolute performance numbers.

Linker

By default, rustc uses the default system linker, which tends to be quite slow. Using lld (LLVM linker) or mold (very new, very fast linker) provides big wins for many setups.

I don't know what's the official source of truth for using alternative linkers, I usually refer to this comment.

Usually, adding

[build]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

to ~/.cargo/config is the most convenient approach.

lld itself can be installed with sudo apt install lld (or the equivalent in the distro/package manager of your choice).

Prebuilt RocksDB

By default, we compile RocksDB (a C++ project) from source during the neard build. By linking to a prebuilt copy of RocksDB this work can be avoided entirely. This is a huge win, especially if you clean the ./target directory frequently.

To use a prebuilt RocksDB, set the ROCKSDB_LIB_DIR environment variable to a location containing librocksdb.a:

$ export ROCKSDB_LIB_DIR=/usr/lib/x86_64-linux-gnu
$ cargo build -p neard

Note, that the system must provide a recent version of the library which, depending on which operating system you’re using, may require installing packages from a testing branch. For example, on Debian it requires installing librocksdb-dev from the experimental repository:

Note: Based on which distro you are using this process will look different. Please refer to the documentation of the package manager you are using.

echo 'deb http://ftp.debian.org/debian experimental main contrib non-free' |
    sudo tee -a /etc/apt/sources.list
sudo apt update
sudo apt -t experimental install librocksdb-dev

ROCKSDB_LIB_DIR=/usr/lib/x86_64-linux-gnu
export ROCKSDB_LIB_DIR

Global Compilation Cache

By default, Rust compiles incrementally, with the incremental cache and intermediate outputs stored in the project-local ./target directory.

The sccache utility can be used to share these artifacts between machines or checkouts within the same machine. sccache works by intercepting calls to rustc and will fetch the cached outputs from the global cache whenever possible. This tool can be set up as such:

$ cargo install sccache
$ export RUSTC_WRAPPER="sccache"
$ export SCCACHE_CACHE_SIZE="30G"
$ cargo build -p neard

Refer to the project’s README for further configuration options.

IDEs Are Bad For Environment Handling

Generally, the knobs in this section are controlled either via global configuration in ~/.cargo/config or environment variables.

Environment variables are notoriously easy to lose, especially if you are working both from a command line and a graphical IDE. Double-check that the environment within which builds are executed is identical to avoid nasty failure modes such as full cache invalidation when switching from the CLI to an IDE or vice-versa.

direnv sometimes can be used to conveniently manage project-specific environment variables.