From 595701dfd90d98cff7ea26bccb581cbc0e612683 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Tue, 3 Nov 2026 00:29:02 +0200 Subject: [PATCH 1/2] ci: link the Linux Rust builds with mold A local action installs mold and points the x86_64 Linux target's rustflags at it in place of the bundled rust-lld, which the compiler otherwise uses regardless of the system linker. C builds keep the system linker. Every Linux job that links host Rust code takes the step; the bench build keeps the flags beside its target-cpu baseline. Jobs whose tooling sets RUSTFLAGS itself (coverage, fuzz) or that build only for other targets are left as they are. On a probe host mold linked the bench example and the debug test binaries with identical output, building the bench example 8% faster cold and writing 14-40% less to the filesystem. --- .github/actions/mold/action.yml | 17 +++++++++++++++++ .github/workflows/ci.yml | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 .github/actions/mold/action.yml diff --git a/.github/actions/mold/action.yml b/.github/actions/mold/action.yml new file mode 100644 index 00000000..7cc6d3d4 --- /dev/null +++ b/.github/actions/mold/action.yml @@ -0,0 +1,17 @@ +name: Link with mold +description: > + Install mold and link this job's x86_64 Linux Rust builds with it in place + of the bundled rust-lld: fewer threads and less random writing. C builds keep + the system linker. MOLD_RUSTFLAGS carries the flags for a step that sets the + target's rustflags itself. +runs: + using: composite + steps: + - uses: rui314/setup-mold@v1 + with: + make-default: false + - shell: bash + run: | + flags="-C linker-features=-lld -C link-arg=-fuse-ld=mold" + echo "MOLD_RUSTFLAGS=$flags" >> "$GITHUB_ENV" + echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=$flags" >> "$GITHUB_ENV" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0716b83d..2be30b17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,6 +121,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7.0.1 + - uses: ./.github/actions/mold - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy @@ -251,6 +252,7 @@ jobs: # Build + test + header-diff job (no push); don't leave the token # in the checkout's git config. persist-credentials: false + - uses: ./.github/actions/mold - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -383,6 +385,8 @@ jobs: steps: - uses: actions/checkout@v7.0.1 if: needs.changes.outputs.code == 'true' + - uses: ./.github/actions/mold + if: needs.changes.outputs.code == 'true' && runner.os == 'Linux' - uses: dtolnay/rust-toolchain@stable if: needs.changes.outputs.code == 'true' - name: Prepend toolchain bin to PATH (macos shim workaround) @@ -460,6 +464,8 @@ jobs: if: needs.changes.outputs.code == 'true' with: persist-credentials: false + - uses: ./.github/actions/mold + if: needs.changes.outputs.code == 'true' && runner.os == 'Linux' - uses: dtolnay/rust-toolchain@stable if: needs.changes.outputs.code == 'true' - name: Prepend toolchain bin to PATH (macos shim workaround) @@ -494,6 +500,8 @@ jobs: if: needs.changes.outputs.code == 'true' with: persist-credentials: false + - uses: ./.github/actions/mold + if: needs.changes.outputs.code == 'true' - uses: dtolnay/rust-toolchain@stable if: needs.changes.outputs.code == 'true' - uses: Swatinem/rust-cache@v2 @@ -539,6 +547,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7.0.1 + - uses: ./.github/actions/mold - uses: dtolnay/rust-toolchain@stable with: toolchain: "1.92.0" @@ -565,6 +574,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7.0.1 + - uses: ./.github/actions/mold - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -854,6 +864,7 @@ jobs: - uses: actions/checkout@v7.0.1 with: persist-credentials: false + - uses: ./.github/actions/mold - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: @@ -1255,6 +1266,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7.0.1 + - uses: ./.github/actions/mold - uses: dtolnay/rust-toolchain@stable - name: Install benchmark target run: rustup target add ${{ matrix.bench.target_triple }} @@ -1290,7 +1302,10 @@ jobs: # decompress/level_-1_fast/decodecorpus-z000033/c_stream on # i9-9900K; the win comes from _bzhi_u64-backed # mask_lower_bits in the FSE state-update hot path. - CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS: "-C target-cpu=x86-64-v3" + # + # The gnu target also keeps the job's mold link flags, which this + # step's own value would otherwise replace. + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS: "-C target-cpu=x86-64-v3 ${{ env.MOLD_RUSTFLAGS }}" CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS: "-C target-cpu=x86-64-v3" run: | # `--no-run` builds without executing; `--message-format=json` From af4766144f8490449701b1f2545a6eb83f9eb549 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Tue, 3 Nov 2026 11:56:15 +0200 Subject: [PATCH 2/2] ci: carry the mold flags into the bench, coverage and fuzz builds - bench-build composes its x86-64-v3 baseline with the mold flags in the script, from the job's environment, rather than through a step env expression. - codecov and the fuzz smoke build through RUSTFLAGS, which overrides the target's rustflags; the mold flags now go in through RUSTFLAGS, and cargo-llvm-cov and cargo-fuzz append their own to them. - The fuzz smoke runs close to twenty minutes on a cold cache and hit its limit; it now has thirty. --- .github/actions/mold/action.yml | 2 +- .github/workflows/ci.yml | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/actions/mold/action.yml b/.github/actions/mold/action.yml index 7cc6d3d4..bdf4f184 100644 --- a/.github/actions/mold/action.yml +++ b/.github/actions/mold/action.yml @@ -3,7 +3,7 @@ description: > Install mold and link this job's x86_64 Linux Rust builds with it in place of the bundled rust-lld: fewer threads and less random writing. C builds keep the system linker. MOLD_RUSTFLAGS carries the flags for a step that sets the - target's rustflags itself. + target's rustflags itself or builds through RUSTFLAGS, which overrides them. runs: using: composite steps: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2be30b17..eb831c15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -943,10 +943,13 @@ jobs: - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@nextest + - uses: ./.github/actions/mold # Through nextest, as the test job runs: a process per test. Some tests # set process-wide state (the CLI's SIGINT disposition), and the plain - # harness would run them as threads of one process. - - run: cargo llvm-cov nextest --profile ci -p structured-zstd --features hash,std,dict-builder --lcov --output-path lcov.info + # harness would run them as threads of one process. cargo-llvm-cov sets + # RUSTFLAGS, which overrides the target's rustflags, so the mold flags + # go in through RUSTFLAGS and it appends its instrumentation to them. + - run: RUSTFLAGS="$MOLD_RUSTFLAGS" cargo llvm-cov nextest --profile ci -p structured-zstd --features hash,std,dict-builder --lcov --output-path lcov.info working-directory: zstd - uses: codecov/codecov-action@v7 with: @@ -991,7 +994,9 @@ jobs: # `-max_total_time` seconds of fresh fuzzing on top. name: Fuzz smoke needs: lint - timeout-minutes: 20 + # Six targets at 90 s each plus the sanitizer builds run close to + # twenty minutes on a cold cache. + timeout-minutes: 30 runs-on: ubuntu-latest env: # Override `rust-toolchain.toml` (which pins stable) so `cargo fuzz` @@ -1012,6 +1017,11 @@ jobs: steps: - uses: actions/checkout@v7.0.1 - uses: dtolnay/rust-toolchain@nightly + - uses: ./.github/actions/mold + # cargo-fuzz builds with RUSTFLAGS of its own, appending any it is + # given, so the mold flags reach it through RUSTFLAGS. + - name: Link the fuzz builds with mold + run: echo "RUSTFLAGS=$MOLD_RUSTFLAGS" >> "$GITHUB_ENV" - uses: Swatinem/rust-cache@v2 with: prefix-key: fuzz @@ -1303,11 +1313,11 @@ jobs: # i9-9900K; the win comes from _bzhi_u64-backed # mask_lower_bits in the FSE state-update hot path. # - # The gnu target also keeps the job's mold link flags, which this - # step's own value would otherwise replace. - CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS: "-C target-cpu=x86-64-v3 ${{ env.MOLD_RUSTFLAGS }}" + # The gnu target's baseline is added in the script below, beside + # the mold link flags the job's environment already holds. CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS: "-C target-cpu=x86-64-v3" run: | + export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C target-cpu=x86-64-v3 $MOLD_RUSTFLAGS" # `--no-run` builds without executing; `--message-format=json` # exposes the resolved binary path in the build log so we # can ship just the executable to shards. Building BOTH