Skip to content

fix(build): use thin LTO to cut peak build memory#367

Closed
dubadub wants to merge 1 commit into
mainfrom
fix/thin-lto-build-memory
Closed

fix(build): use thin LTO to cut peak build memory#367
dubadub wants to merge 1 commit into
mainfrom
fix/thin-lto-build-memory

Conversation

@dubadub

@dubadub dubadub commented Jul 13, 2026

Copy link
Copy Markdown
Member

Fixes the cargo install failure reported in #366.

Problem

error: failed to load bitcode of module "cooklang_find-...cgu.0.rcgu.o":

That message is emitted from exactly one place in rustc — the fat LTO path (run_fat in rustc_codegen_llvm/src/back/lto.rs). We opt into it ourselves:

[profile.release]
lto = true          # fat LTO
codegen-units = 1

Fat LTO with codegen-units = 1 is the highest-peak-memory codegen configuration available, and we apply it across a ~400 crate graph. Note the reporter's error ends with ": and an empty LLVM message — LLVM was handed a buffer that is not valid bitcode (a truncated .rlib), rather than hitting a codegen bug, which would print a concrete diagnostic like Invalid cast.

The truncation has a clear source on their platform: cargo install writes its target dir under $TMPDIR (hence /tmp/cargo-installwdiPPp), and Armbian mounts /tmp on zram — RAM-backed, sized to RAM/2 by default (armbian-zram-config). So 1.3 GB of build artifacts land in RAM on a board that simultaneously needs a multi-GB heap for fat LTO.

Change

lto = "thin", and drop the codegen-units = 1 pin (back to the release default of 16). Thin LTO keeps most of the cross-crate optimisation benefit without the fat-LTO memory cliff, and restores codegen parallelism.

Verification

Release build succeeds; resulting binary is 23 MB on aarch64-apple-darwin. I did not complete a clean fat-LTO size comparison locally, so if binary size is a hard constraint here, worth checking CI's artifact sizes before merging — some size regression is expected and is the intended trade for not OOMing on a 1–2 GB SBC.

Refs #366

@dubadub

dubadub commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Binary size comparison now measured (aarch64-apple-darwin, release, strip = true):

profile binary
lto = true + codegen-units = 1 (old) 19.6 MB
lto = "thin", default codegen-units (this PR) 23.2 MB

So this costs +3.6 MB, +18%. That is not nothing, and it is the honest trade: a somewhat larger binary in exchange for cargo install not OOMing on a 1–2 GB SBC.

If the size regression is unacceptable, the middle option is lto = "thin" with codegen-units = 1 retained — that should claw back most of the size while still avoiding the fat-LTO memory cliff (which is the actual cause of #366), at the cost of codegen parallelism. Happy to switch to that if preferred; say the word and I will measure it.

Worth noting the size question mostly affects people downloading release binaries, who are unaffected by the build-memory problem either way.

@pinage404

Copy link
Copy Markdown
Contributor

I recommend you to check this https://github.com/johnthagen/min-sized-rust

Fat LTO with codegen-units = 1 is the highest-peak-memory codegen
configuration available, and applying it across a ~400 crate graph needs
a multi-GB heap at link time. On low-memory ARM boards this OOMs, and
because cargo install writes its target dir into /tmp -- which Armbian
mounts on zram, sized to RAM/2 -- the resulting truncated rlib surfaces
as 'failed to load bitcode of module'.

Thin LTO keeps most of the optimisation benefit without the fat LTO
memory cliff, and dropping codegen-units = 1 restores codegen
parallelism.

Refs #366
@dubadub dubadub force-pushed the fix/thin-lto-build-memory branch from 2274813 to 485bd96 Compare July 14, 2026 11:37
@dubadub

dubadub commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

Closing: we are keeping fat LTO.

The +18% binary size (19.6 → 23.2 MB) is not worth paying on every release download to fix a problem that only affects people compiling from source on memory-constrained machines — and those people now have a documented escape hatch.

#368 (merged) tells them to keep the build off the RAM-backed /tmp, which is the actual cause:

CARGO_TARGET_DIR=~/.cache/cookcli-build cargo install cookcli --locked

and, if memory is still tight, to turn off LTO for their own build:

CARGO_PROFILE_RELEASE_LTO=false CARGO_TARGET_DIR=~/.cache/cookcli-build cargo install cookcli --locked

The better answer for SBC users remains the prebuilt aarch64 and armv7 binaries we already publish, which #368 now points them at first.

The diagnosis in this PR still stands and is worth keeping on record: the error in #366 is only reachable through the fat-LTO path, and it fires because cargo install puts its target dir under $TMPDIR, which Armbian mounts on zram at RAM/2.

@dubadub dubadub closed this Jul 14, 2026
@dubadub dubadub deleted the fix/thin-lto-build-memory branch July 14, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants