From 10b99c6f55929a9f4a668fc4e81902b917e1c48a Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Thu, 25 Sep 2025 01:35:13 -0700 Subject: [PATCH] RFC: Recommend and enable using Wild rather than Mold on Linux for local builds (#37717) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Summary Today, Zed uses Mold on Linux, but Wild can be significantly faster. On my machine, Wild is 14% faster at a whole-tree clean build, 20% faster on an incremental build with a minimal change, and makes no measurable effect on runtime performance of tests. However, Wild's page says it's not yet ready for production, so it seems to early to switch for production and CI builds. This PR keeps using Mold in CI and lets developers choose in their own config what linker to use. (The downside of this is that after landing this change, developers will have to do some local config or it will fall back to the default linker which may be slower.) [Wild 0.6 is out, and their announcement has some benchmarks](https://davidlattimore.github.io/posts/2025/09/23/wild-update-0.6.0.html). cc @davidlattimore from Wild, just fyi # Tasks - [x] Measure Wild build, incremental build, and runtime performance in different scenarios - [x] Remove the Linux linker config from `.cargo/config.toml` in the tree - [x] Test rope benchmarks etc - [x] Set the linker to Mold in CI - [x] Add instructions to use Wild or Mold into `linux.md` - [x] Add a script to download Wild - [x] Measure binary size - [x] Recommend Wild from `scripts/linux` # Benchmarks | | wild 0.6 (rust 1.89) | mold 2.37.1 (1.89) | lld (rust 1.90) | wild advantage | | -- | -- | -- | -- | -- | | clean workspace build | 176s | 184s | 182s | 5% faster than mold | | nextest run workspace after build | 137s | 142s | 137s | in the noise? | | incremental rebuild | 3.9s | 5.0s | 6.6s | 22% faster than mold | I didn't observe any apparent significant change in runtime performance or binary size, or in the in-tree microbenchmarks. Release Notes: - N/A --------- Co-authored-by: Mateusz MikuĊ‚a --- .cargo/ci-config.toml | 12 ++++++++++++ .cargo/config.toml | 8 -------- docs/src/development/linux.md | 27 +++++++++++++++++++++++++++ script/install-wild | 29 +++++++++++++++++++++++++++++ script/linux | 23 +++++++++++++++++------ 5 files changed, 85 insertions(+), 14 deletions(-) create mode 100755 script/install-wild diff --git a/.cargo/ci-config.toml b/.cargo/ci-config.toml index 09e1af5c18174f5b1024e223f4a0cd5dac256c6e..d5e312c2429ad8a4fa933d4080c8fcde217bd6eb 100644 --- a/.cargo/ci-config.toml +++ b/.cargo/ci-config.toml @@ -10,3 +10,15 @@ # Here, we opted to use `[target.'cfg(all())']` instead of `[build]` because `[target.'**']` is guaranteed to be cumulative. [target.'cfg(all())'] rustflags = ["-D", "warnings"] + +# Use Mold on Linux, because it's faster than GNU ld and LLD. +# +# We no longer set this in the default `config.toml` so that developers can opt in to Wild, which +# is faster than Mold, in their own ~/.cargo/config.toml. +[target.x86_64-unknown-linux-gnu] +linker = "clang" +rustflags = ["-C", "link-arg=-fuse-ld=mold"] + +[target.aarch64-unknown-linux-gnu] +linker = "clang" +rustflags = ["-C", "link-arg=-fuse-ld=mold"] diff --git a/.cargo/config.toml b/.cargo/config.toml index a882cd67bf4040ecaead1f8cce9ffe7a2f96d553..7073c6db55e17dbc7541207a2f311ca30e390a69 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,14 +7,6 @@ xtask = "run --package xtask --" perf-test = ["test", "--profile", "release-fast", "--lib", "--bins", "--tests", "--all-features", "--config", "target.'cfg(true)'.runner='cargo run -p perf --release'", "--config", "target.'cfg(true)'.rustflags=[\"--cfg\", \"perf_enabled\"]"] perf-compare = ["run", "--release", "-p", "perf", "--", "compare"] -[target.x86_64-unknown-linux-gnu] -linker = "clang" -rustflags = ["-C", "link-arg=-fuse-ld=mold"] - -[target.aarch64-unknown-linux-gnu] -linker = "clang" -rustflags = ["-C", "link-arg=-fuse-ld=mold"] - [target.'cfg(target_os = "windows")'] rustflags = [ "--cfg", diff --git a/docs/src/development/linux.md b/docs/src/development/linux.md index a0f9af5f264d7a90bea98dcf567156a414ad8216..a6799378bc00b5c992fd99562b6446729de22559 100644 --- a/docs/src/development/linux.md +++ b/docs/src/development/linux.md @@ -20,6 +20,33 @@ Clone down the [Zed repository](https://github.com/zed-industries/zed). If you are looking to develop Zed collaboration features using a local collaboration server, please see: [Local Collaboration](./local-collaboration.md) docs. +### Linkers {#linker} + +On Linux, Rust's default linker is [LLVM's `lld`](https://blog.rust-lang.org/2025/09/18/Rust-1.90.0/). Alternative linkers, especially [Wild](https://github.com/davidlattimore/wild) and [Mold](https://github.com/rui314/mold) can significantly improve clean and incremental build time. + +At present Zed uses Mold in CI because it's more mature. For local development Wild is recommended because it's 5-20% faster than Mold. + +These linkers can be installed with `script/install-mold` and `script/install-wild`. + +To use Wild as your default, add these lines to your `~/.cargo/config.toml`: + +```toml +[target.x86_64-unknown-linux-gnu] +linker = "clang" +rustflags = ["-C", "link-arg=--ld-path=wild"] + +[target.aarch64-unknown-linux-gnu] +linker = "clang" +rustflags = ["-C", "link-arg=--ld-path=wild"] +``` + +To use Mold as your default: + +```toml +[target.'cfg(target_os = "linux")'] +rustflags = ["-C", "link-arg=-fuse-ld=mold"] +``` + ## Building from source Once the dependencies are installed, you can build Zed using [Cargo](https://doc.rust-lang.org/cargo/). diff --git a/script/install-wild b/script/install-wild new file mode 100755 index 0000000000000000000000000000000000000000..7e35452db7f627378e8d4ab660187eb8fcdbeca2 --- /dev/null +++ b/script/install-wild @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Install wild-linker official binaries from GitHub Releases. + +set -euo pipefail + +WILD_VERSION="${WILD_VERSION:-${1:-0.6.0}}" +if [ "$(uname -s)" != "Linux" ]; then + echo "Error: This script is intended for Linux systems only." + exit 1 +elif [ -z "$WILD_VERSION" ]; then + echo "Usage: $0 [version]" + exit 1 +elif which -s wild && wild --version | grep -Fq "$WILD_VERSION" ; then + echo "Warning: existing wild $WILD_VERSION found at $(which wild). Skipping installation." + exit 0 +fi + +if [ "$(whoami)" = root ]; then SUDO=; else SUDO="$(command -v sudo || command -v doas || true)"; fi + +ARCH="$(uname -m)" +WILD_REPO="${WILD_REPO:-https://github.com/davidlattimore/wild}" +WILD_PACKAGE="wild-linker-${ARCH}-unknown-linux-gnu" +WILD_URL="${WILD_URL:-$WILD_REPO}/releases/download/$WILD_VERSION/${WILD_PACKAGE}.tar.gz" + +echo "Downloading from $WILD_URL" +curl -fsSL --output - "$WILD_URL" \ + | $SUDO tar -C /usr/local/bin --strip-components=1 --no-overwrite-dir -xJf - \ + "wild-linker-${ARCH}-unknown-linux-gnu/wild" diff --git a/script/linux b/script/linux index 5523d876871a1d69e22b80344a02785626f1faad..3d619858d1b042148f6950488f62cbf34a8355c7 100755 --- a/script/linux +++ b/script/linux @@ -11,12 +11,23 @@ fi function finalize { # after packages install (curl, etc), get the rust toolchain which rustup > /dev/null 2>&1 || curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - # verify the mold situation - if ! command -v mold >/dev/null 2>&1; then - echo "Warning: Mold binaries are unavailable on your system." >&2 - echo " Builds will be slower without mold. Try: script/install-mold" >&2 - fi - echo "Finished installing Linux dependencies with script/linux" + cat <