Skip to content

30% cargo target directory size regression between rust 1.96(.1) and 1.97(.1) #160659

Open

Description

@inga-lovinde

On our codebase (~1M LoC in a workspace with ~700 crates), we noticed a significant cargo target directory size regression after upgrading to Rust 1.97.1.

Examples of what we're observing:

  • cargo build --bins --release with default optimization settings (there are no [profile.release] section in our Cargo.toml) and latest mold linker: increase from ~6GB to ~7GB.
  • cargo nextest archive (which as I understand simply builds everything with all features in debug profile), with CARGO_INCREMENTAL environment variable set to 1 and RUSTFLAGS environment variable set to -C strip=debuginfo and necessary mold flags, and with debug = 0 in [profile.dev]: increase from ~62GB to ~79GB.
  • cargo clippy --all-targets or cargo clippy --bins with CARGO_INCREMENTAL=1 and mold in RUSTFLAGS: no difference between 1.96.1 and 1.97.1 (at ~19GB and ~11GB respectively).

The results are very reproducible; just changing channel in rust-toolchain.toml between 1.96.1 and 1.97.1 (and cleaning cargo target directory so that it's empty) and re-running the command is enough to see the old or the new size. This also reproduces in a clean container based on rust:1.96.1-bookworm or rust:1.97.1-bookworm images (with regular libraries necessary for build installed from regular debian package repository).

Comparing two cargo target directories (for cargo nextest archive example) does not produce any insights; the directory structures are the same, and it's just that every file is suddely 20-50% larger on 1.97.1. debug directory is larger, everything in debug/deps is larger, and everything in debug/incremental/<crate_id>/<id> is larger (including dep-graph.bin and query-cache.bin).

Interestingly, archiving and compressing the target directory with ZSTD_CLEVEL=12 tar --zstd -cf ... produces archives of more or less the same size (~16.5GB), hinting that all that extra disk space used does not actually contain any significant amounts of new information; that the extra data is somewhat redundant from zstd point of view.

So I suspect that maybe this was caused by the change in #151994 . Maybe with the new mangling scheme, somehow our identifiers are so large (and there are so many of them in the output) that on non-incremental release builds they contribute 15% to the size of cargo target dir, and on incremental builds, 30%?

I'm sorry that this is not a very detailed or specific information.

Version it worked on

It most recently worked on: 1.96.1

Version with regression

rustc --version --verbose:

rustc 1.97.1 (8bab26f4f 2026-07-14)
binary: rustc
commit-hash: 8bab26f4f68e0e26f0bb7960be334d5b520ea452
commit-date: 2026-07-14
host: x86_64-unknown-linux-gnu
release: 1.97.1
LLVM version: 22.1.6

@rustbot modify labels: +regression-from-stable-to-stable -regression-untriaged

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    and removed
    regression-untriagedUntriaged performance or correctness regression.
    on Aug 6, 2026
  2. inga-lovinde commented on Aug 6, 2026

    @inga-lovinde
    Author

    After reading https://blog.rust-lang.org/2025/11/20/switching-to-v0-mangling-on-nightly/ , decided to try to enable -C strip=symbols.

    Results:

    • On 1.96.1, with -C strip=debuginfo: 62GB.
    • On 1.96.1, with -C strip=symbols: 53GB (9GB less).
    • On 1.97.1, with -C strip=debuginfo: 79GB.
    • On 1.97.1, with -C strip=symbols: 62GB (17GB less than without stripping symbols, but still 9GB more than on 1.96.1 with stripping symbols).
  3. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    A-name-manglingArea: Name mangling / decoration aka symbol mangling
    on Aug 7, 2026
  4. apiraino commented on Aug 11, 2026

    @apiraino
    Contributor

    Visited during compiler pre-triage: I wonder if there's anything else we can do to recover these binary size regressions caused by the new v0 mangling

    @rustbot label i-compiler-nominated

  5. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Aug 13, 2026
  6. apiraino commented on Aug 13, 2026

    @apiraino
    Contributor

    Visited during triage on Zulip.

    Currently we don't have other actionable for this than tuning strip flag (as pointed out in the blog post), if you have any questions about passing them to cargo, let us know.

    We discussed how we could explore in the future other ways to strip symbols - that's open to anyone having bandwidth to tackle the problem - but nothing we can offer right now.

    @rustbot label -i-compiler-nominated -i-prioritize +p-medium

  7. added and removed
    I-compiler-nominatedNominated for discussion during a compiler team meeting.
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Aug 13, 2026
  8. inga-lovinde commented on Aug 13, 2026

    @inga-lovinde
    Author

    Currently we don't have other actionable for this than tuning strip flag (as pointed out in the blog post), if you have any questions about passing them to cargo, let us know.

    Not sure I understand; do you mean passing something even more aggressive than strip=symbols?

    Just wanted to make it clear that the regression is there even with -C strip=symbols (for incremental builds): 62GB on 1.97.1 vs 53GB on 1.96.1, or ~17% increase after upgrading to 1.97.1 (which is less than 30% increase after upgrading when only debuginfo is stripped, but still).

    (I understand that this problem is not actionable by the compiler team now.)

  9. davidlattimore commented on Aug 13, 2026

    @davidlattimore
    Contributor

    It makes sense that strip=symbols would only recover some of the size, since that option will strip the symbols from the final binary, but the symbol names still need to exist in the object files being passed to the linker, so still take up space in the target directory.

    With a non-stripped build, you can find the longest symbol name in your binary with a command like:

    nm target/release/{bin-name} | cut -c20- | awk '{print length " " $0}' | sort -n | tail -1 | rustfilt
    

    Most likely it'll be something with lots of generics and closures.

    There was mention in the Zulip thread of the possibility of having an option to reduce symbol names to just hashes. A less drastic option might be to reduce the parts of the symbol name that represent closures to hashes.

  10. inga-lovinde commented on Aug 14, 2026

    @inga-lovinde
    Author

    It makes sense that strip=symbols would only recover some of the size, since that option will strip the symbols from the final binary, but the symbol names still need to exist in the object files being passed to the linker, so still take up space in the target directory.

    Yes, this was my assumption.

    Most likely it'll be something with lots of generics and closures.

    I'm running it on the .o files in target/debug/incremental, and for some of them get mostly generics (mostly diesel generics specifically, some of them 10k+ characters long), yes.
    For other quite large .o files it's less interesting; for example, for one of them the longest symbol is just 542 characters (after rustfilt), half of which is just a fully qualified name of a test method with a very verbose name nested deep in the module hierarchy.
    Still, perhaps (just as an idea for the far future) there could be another optional mangling scheme that instead of these full names only produces, say, their SHA-256 checksums, for scenarios in which it is important to make cargo target directory smaller (e.g. for building on systems with limited storage)?

    But also that's just .o files. Around 70 to 80% of target/debug/incremental by weight is depgraph.bin and query-cache.bin and metadata.rmeta files, not .o files.

  11. bjorn3 commented on Sep 15, 2026

    @bjorn3
    Member

    The .rmeta files contains a list of all exported symbols. And the query cache might too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-name-manglingArea: Name mangling / decoration aka symbol manglingC-bugCategory: This is a bug.P-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions