AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

A faster Rust linker still needs a shared runner budget

Rust 1.90 changed the Linux linker default for one target. Assess shared runner throughput, resource contention and fallback before increasing CI concurrency.

Citiți articolul în română

A build that finishes sooner on an idle machine can still be a poor neighbour on a crowded runner. Rust's September 2025 linker change is a good reason to revisit the resource budget of shared CI. Adopt the faster default where it fits, but judge the result by completed work across the runner and keep an explicit fallback. A shorter individual link does not establish that more simultaneous builds will finish sooner.

Looking back from November 2026, the Rust 1.90 announcement of 18 September 2025 made LLD the default for x86_64-unknown-linux-gnu. That target boundary matters. The release did not move every Rust target, every Linux platform or every custom toolchain to a universal new linker policy. Inventory the actual target and configuration before deciding whether a runner changed at all.

The default changes one stage of a build

The advance notice of 1 September explains that rustup-distributed rustc includes the linker as rust-lld. It also warns that parallel linking can be undesirable in constrained environments. That warning supplies the operational question: how much work can this machine support when several builds reach their demanding stages together?

Compilation, linking and testing occupy different portions of a job. Improving one stage changes how those portions overlap across jobs. If several builds previously spent a long time linking with fewer active threads, a more parallel linker can produce shorter but more concentrated demand. This is a scheduling hypothesis derived from the documented caveat, not a measurement of a particular runner or a claim that LLD necessarily consumes more memory in every project.

The Rust authors reported substantial improvements for their ripgrep example. Those are author-reported results for a named workload, not an AL+ benchmark or a forecast for another repository. Their useful message is that linking can be worth attention. They cannot tell a team how many of its own builds to admit to a machine already running tests and other repositories.

Choose the unit that matches the queue

For a shared runner, the proposed comparison should begin when work becomes eligible and end when the agreed set of jobs completes. Record queue waiting separately from execution. Otherwise a configuration can look faster in each job's execution chart while people wait longer for a slot. Both observations can be true without either measurement being wrong.

Hold the workload definition steady. Use the same revision, target, build profile and cache policy when comparing configurations. Distinguish an incremental rebuild from a clean build because their stage balance differs. Mixing those cases into an unexplained average can make a change in workload look like a linker improvement or regression. These are proposed evaluation conditions; no Rust build or runner experiment was performed for this article.

Resource observations should accompany elapsed time. Peak memory, CPU pressure and failed or retried jobs help explain whether a faster stage is transferring cost elsewhere. A machine that completes one benchmark quickly but repeatedly kills neighbouring jobs is serving a different objective from a runner intended to clear a reliable queue. Set the acceptance criterion before interpreting the attractive timing.

The concurrency budget belongs to the whole runner. A per-build job limit cannot by itself describe demand from several independent CI jobs. Name the layer responsible for admission, and account for other work allowed onto the host. The useful policy might be fewer simultaneous builds, a separate class for demanding jobs or the existing limit retained while the new default is observed. The sources do not select that policy for you.

Keep fallback narrower than a toolchain rollback

The release announcement documents -C linker-features=-lld as the opt-out to the system linker for this target. Preserve that exact scope when recording a fallback. It is a way to isolate a linker-specific problem; it is not a promise to undo unrelated compiler changes or reproduce every property of a previous toolchain.

Give the fallback an owner

A fallback also needs a reason and an owner. If a project depends on behaviour that differs between GNU ld and LLD, retain the failing evidence and track the compatibility issue. If the problem is runner contention, changing admission may be a more useful response than permanently abandoning the faster linker. Diagnose which boundary failed before turning an emergency setting into an unexplained convention.

Pinning a toolchain can make that comparison intelligible. An unrecorded compiler update, changed cache and altered runner size in the same evaluation leave several plausible explanations. The aim is a decision that another maintainer can reconstruct, with the selected target and fallback visible in the maintenance record. This article proposes no configuration change or executable migration recipe.

Spare capacity changes the decision

The strongest counterargument is a developer workstation or dedicated runner with ample headroom. There may be little reason to delay a useful default behind a large capacity study. An ordinary compatibility check and observation of the actual project can be proportionate. A warning about constrained hosts does not establish a regression on unconstrained ones.

That exception strengthens the case for defining the operating environment. The same repository can benefit on a dedicated machine while needing a different admission limit in a shared pool. For custom software development, include runner capacity and fallback ownership in the handover. Accept the faster linker when the intended queue clears reliably within its resource budget, and revisit concurrency only with evidence about that queue.