这是一份实验报告,不是“最佳实践清单”。我想知道的是:网上流传的那些 Rust 编译提速与减体积建议,在这台机器上到底各自值多少。所以每一项都做了控制变量的实测,每个关键配置重复 3 次,报告原始数据而不只是结论。

最重要的两个发现提前说:

  1. 日常开发真正的加速不是加旗子,而是别用 release 迭代。 改一行代码,dev profile 重编译 1.19 秒;开了 lto = true 的 release 要 18.27 秒——和“完全没改过”的 18.17 秒几乎一样,也就是 release 根本没有增量编译可用。
  2. Windows 上 strip = "symbols" 是完全的无效操作。 开与关,exe 和 .pdb 的字节数一模一样。

下面是完整过程。

一、测试环境

Terminal window
rustc -vV
# rustc 1.98.1 (48a229cea 2026-09-01)
# host: x86_64-pc-windows-msvc
# LLVM version: 22.1.8
cargo --version # cargo 1.98.1 (797e8a9bc 2026-08-05)
rustc +nightly --version
# rustc 1.100.0-nightly (feaadeeac 2026-09-19)
cargo +nightly --version
# cargo 1.100.0-nightly (495c385d9 2026-09-16)
rustup --version # rustup 1.29.1 (d95a37b6a 2026-08-13)
nproc # 16
项 值
操作系统 Windows 10.0.19045 (x64)
CPU 16 个逻辑核
工具链 stable 1.98.1 + nightly 1.100.0
后端 LLVM 22.1.8
磁盘 C: 301 GB,测试期间剩余 93 GB

环境勘察还确认了三件对实验有影响的事:

Terminal window
# 1. rust-lld 已经随 rustup 装在 sysroot 里,但不在 PATH 上
rustc --print sysroot
ls "$(rustc --print sysroot)/lib/rustlib/x86_64-pc-windows-msvc/bin"
# gcc-ld rust-lld.exe rust-objcopy.exe wasm-component-ld.exe ...
# 2. 系统 PATH 里没有 lld-link(VS 的 LLVM 组件没装)
where lld-link # not on PATH
# 3. 全局 Cargo 配置是空的,本实验不受既有配置污染
ls ~/.cargo/config.toml # (no such file)

二、实验设计

2.1 被测对象

用途 项目 理由
编译速度 du-dust 1.2.6(dust) 真实项目、74 个依赖、自带 [profile.release] lto=true, codegen-units=1, strip=true,正好能对比“项目自带的重优化 profile”和“关掉优化的 profile”;源码已在本地 registry 缓存里,可离线复现
产物体积 自建单 crate(纯 std、零依赖) 体积实验需要隔离单个开关的影响。真实项目的 profile 是一整套,分不清哪个开关贡献了多少

du-dust 的 release profile 原文(来自 crate 里的 Cargo.toml):

[profile.release]
lto = true
codegen-units = 1
strip = true

源码副本的获取方式(不污染 registry,改完即弃):

Terminal window
cp -r ~/.cargo/registry/src/index.crates.io-*/du-dust-1.2.6 dust2
cd dust2 && rm -f Cargo.lock

删掉 Cargo.lock 是为了让 Cargo 在本地重新解析依赖,避免继承打包时的锁文件造成不可预期的差异。

2.2 冷编译的定义与测量方法

  • 冷编译=用一个全新的 --target-dir 编译,所有依赖从零构建。每测一轮先 rm -rf 该目录,确保不是复用了上一轮的产物。
  • 计时来源=Cargo 自己打印的 Finished ... in Xs,不自己写秒表。原因很实际:我最初用 date +%s%3N 做 shell 计时,在这套 MSYS 环境下取值异常(差值算出了 1.6e18 ms 这种数字),而 Cargo 的输出精度到 10 ms,够用且没有 shell 层噪声。
  • 每档 3 次,报告 中位数(抗离群值)与极差(观察稳定性)。单次测量在这类“几十秒级、受 OS 文件缓存和杀软影响”的基准上不足以支撑结论。
  • 控制变量:测量期间不跑其它编译任务;所有命令带 --offline,杜绝网络抖动;每档独立 target 目录,避免互相污染指纹。

2.3 重复测量与匹配对照

需要注意一处设计缺陷并说明我的补测方法:nightly 与 stable 是不同版本,直接比较「stable 默认 41.66s vs nightly 并行 30.43s」会把“版本差异”算进“并行前端”的功劳。所以我额外补了 pairing 对照——在同一个 nightly 版本上分别测“不开并行前端”和“开并行前端”,只有这组对比才能归因给 -Zthreads。

三、实验 A:编译速度

A1. 五个配置 × 三次冷编译

Terminal window
# 默认(项目自带 lto=true, cgu=1, strip=true)
cargo build --release --offline --target-dir tA
# 关掉 LTO,放开 codegen-units
cargo build --release --offline --target-dir tB \
--config profile.release.lto=false \
--config profile.release.codegen-units=16
# thin LTO + 放开 codegen-units
cargo build --release --offline --target-dir tC \
--config 'profile.release.lto="thin"' \
--config profile.release.codegen-units=16
# nightly 并行前端
RUSTFLAGS=-Zthreads=16 cargo +nightly build --release --offline --target-dir tD
# 只检查类型,不产码
cargo check --release --offline --target-dir tE

原始数据(单位:秒):

配置 第 1 次 第 2 次 第 3 次 中位数 极差 相对默认
stable 默认(fat LTO, cgu=1) 41.62 42.44 41.66 41.66 0.82 —
stable lto=false, cgu=16 26.48 27.75 26.45 26.48 1.30 −36.4%
stable lto="thin", cgu=16 27.92 26.60 26.57 26.60 1.35 −36.2%
nightly 默认(并行关闭) 39.64 39.61 40.77 39.64 1.16 −4.8%(跨版本)
nightly -Zthreads=16 31.14 30.39 30.43 30.43 0.75 −26.9%(跨版本)
stable cargo check 19.33 18.83 18.92 18.92 0.50 −54.6%

产物大小(三个版本各自 3 次完全一致,这是个很好的可复现性证据):

配置 exe 字节数 可读
默认 2,884,096 2.75 MiB
lto=false, cgu=16 3,146,752 3.00 MiB
thin, cgu=16 3,253,760 3.10 MiB
nightly -Zthreads=16 2,892,288 2.76 MiB

配对结论(同类项对比):

  • 同版本内,nightly 开并行前端 39.64s → 30.43s = −23.2%,这才是 -Zthreads 的真实收益。
  • stable 内,关掉 fat LTO 26.48s vs 默认 41.66s = −36.4%,代价是 exe 从 2.75 MiB 涨到 3.00 MiB(+9.1%)。
  • thin LTO 与“完全关掉 LTO”几乎打平(26.60 vs 26.48,落在极差内),但产物反而大 3.4%(3.10 MiB vs 3.00 MiB)。也就是说在这个项目上,thin LTO 既没换来编译速度,也没换来体积,是纯亏的选项。这条与“thin 是速度与体积的良好折中”的流行说法相反,我按原始数据记录。
  • cargo check 只花 18.92s,比完整 release 快 54.6%,足够过类型系统与借用检查。

A2. 热编译与“改一行”:release 没有增量

场景 第 1 次 第 2 次 第 3 次 中位数
release,源码无改动,重复 build 18.72 18.17 18.15 18.17
release,touch src/main.rs 后 build 18.27 18.40 18.23 18.27
dev,源码无改动,重复 build 1.08 1.11 1.13 1.11
dev,touch src/main.rs 后 build 1.17 2.00 1.19 1.19

「改一行」相对「什么都没改」的代价:

  • release:+0.10s(+0.5%)
  • dev:+0.08s(+7%)

release 的这两个数字在噪声内无法区分。含义很直接:release 构建没有“增量”这回事,每次都是全量重链,你改 main.rs 里的一个数字,和什么都不改,付出的时间一样多(约 18 秒)。dev 侧则是 1.1 秒级的真增量(其中一次 2.00s 是离群值,可能是杀软扫描新生成的 exe)。

dev 的产物是 7,168,000 字节(6.84 MiB),release 默认档是 2,884,096 字节(2.75 MiB),差 2.5 倍。

A3. 为什么 release 无法复用缓存:证据

用 -v 打开详细输出,在一个“什么都没改”的 release 构建上观察:

Terminal window
cargo build --release --offline --target-dir tA -v
# ...
# Running `...\tA\release\build\du-dust-c2b9546718135350\build-script-build`

即使零改动,日志里依然出现 Running ...build-script-build。这是 Cargo 的保守行为:build.rs 如果没有输出 cargo:rerun-if-changed=... 声明自己的输入,Cargo 只好每次都重跑它。

于是链条是这样的:

  1. build.rs 每次重跑 → 触发下游重新判定;
  2. 项目 profile 里有 lto = true + codegen-units = 1,最终二进制必须重新做跨 crate 优化并重新链接 → 依赖的 .rlib 虽然命中缓存,但最后一步省不掉。

两者叠加,得到 18 秒的“改一行”。这不是 Cargo 退化,是 LTO 的固有代价。想保留秒级迭代,选择只有两个:用 dev,或者把 release 的 lto 换成 thin/false。

A4. 链接器:rust-lld 在这个项目上零收益

rust-lld.exe 就在 sysroot 里,rustc 能按名字找到它,实测两种写法都能工作:

Terminal window
# 写法一:按名字(rustc 自己在 sysroot 里查)
cargo build --release --offline --target-dir t-lld \
--config 'target.x86_64-pc-windows-msvc.linker="rust-lld"'
# 写法二:绝对路径
cargo build --release --offline --target-dir tl2 \
--config "target.x86_64-pc-windows-msvc.linker=\"$(cygpath -m "$(rustc --print sysroot)")/lib/rustlib/x86_64-pc-windows-msvc/bin/rust-lld.exe\""

结果:

配置 冷编译 产物
默认 profile 41.66s(3 次中位) 2,884,096 B
默认 profile + rust-lld 42.35s(单次) 2,881,536 B

差异 +1.7%,落在测量噪声里,没有收益。产物大小也对得上(差 2,560 字节,属于链接器版本差异)。

原因不难理解:这个项目只有一个最终二进制,74 个依赖全部以 .rlib 参与,几乎不调用系统链接器。rust-lld 的收益场景是“链接阶段占大头”的工程——大量 [[bin]]、大量测试目标、build.rs 生成多个可执行文件的项目。判断方法:看构建日志里链接阶段的占比,或者数一数项目里有多少个 bin/test 目标。

A5. nightly 并行前端:flag 名有坑

Terminal window
RUSTFLAGS="-Zthreads=16" cargo +nightly build --release

收益已在上文配对给出:−23.2%。

但这里有个坑。在 1.100-nightly 上,-Z help 会提示该选项已改名:

-Z threads=val -- use `--jobs-frontend` instead

照着提示写会直接构建失败,而且失败点很反直觉——不是编译报错,而是在探测阶段:

error: failed to run `rustc` to learn about target-specific information
Caused by:
process didn't exit successfully: `rustc.exe - --crate-name ___ --print=file-names
-Zjobs-frontend=16 --crate-type bin ...` (exit code: 1)
--- stderr
error: unknown unstable option: `jobs-frontend`

正确写法只有两种:

Terminal window
RUSTFLAGS="-Zthreads=16" # 旧名仍可用,实测有效
RUSTFLAGS="-Zunstable-options --jobs-frontend=16" # 新名,但必须同时给 -Zunstable-options

另需注意:并行前端是单个 crate 内部的前端工作并行,而 Cargo 本来就会并行编译多个 crate。所以在“依赖多、单个 crate 小”的项目上收益有限,收益最大的是“依赖很少但单个 crate 巨大”的项目。

A6. Cranelift 后端:快,但产物不可接受

Terminal window
rustup component add rustc-codegen-cranelift --toolchain nightly
RUSTFLAGS="-Zcodegen-backend=cranelift" cargo +nightly build --release --offline \
--target-dir t-cranelift \
--config profile.release.lto=false \
--config profile.release.codegen-units=16
配置 冷编译 产物
nightly + LLVM(lto=off, cgu=16) 26.48s(3 次中位) 3,146,752 B(3.00 MiB)
nightly + Cranelift(同配置) 23.68s(单次) 8,399,360 B(8.01 MiB)

编译快 10.6%(单次测量,仅供参考),产物体积 2.7 倍。

产物做了功能验证(这点很重要,因为“能编译”不等于“能跑”):

Terminal window
cmd.exe /c "t-cranelift\release\dust.exe --version"
# Dust 1.2.6
cmd.exe /c "t-cranelift\release\dust.exe -n 1 src"
# 128Ki ┌─┴ src ...

能正常工作。但 8 MiB 的二进制换 10% 编译时间,只适合 CI 里的快速编译校验,不适合出包。

A7. 一个环境坑:MSYS bash 无法直接执行产物

实测中我发现无法用相对或绝对路径执行刚编出来的 exe:

Terminal window
./t-base/release/dust.exe --version
# error: command not found: ./t-base/release/dust.exe (rc=127)
/c/Users/.../t-base/release/dust.exe --version
# error: command not found (rc=127)

但走 PATH 或 cmd.exe 就正常:

Terminal window
PATH="$PWD/t-base/release:$PATH" dust.exe --version
# Dust 1.2.6
cmd.exe /c "t-base\release\dust.exe --version"
# Dust 1.2.6

文件本身是合法的 PE32+ executable ... x86-64(file 确认),所以这不是产物问题,而是这套 MSYS 环境对带路径的 exe 执行有限制。如果你复现时遇到同样的 rc=127,别怀疑编译,换 PATH 或 cmd.exe 调用即可。

四、实验 B:exe 体积的逐项消融

B1. 设计

真实项目的 release profile 是一整套开关,无法回答“每个开关各值多少”。所以另建一个零依赖的单 crate,用多个继承自 release 的 profile,每次只动一个变量:

[package]
name = "size-bench"
version = "0.1.0"
edition = "2021"
[profile.release] # 默认:opt-level 3, cgu 16, 无 LTO, unwind, 不 strip
[profile.p-lto] # 只加 LTO
inherits = "release"
lto = "fat"
[profile.p-cgu] # 只加 codegen-units=1
inherits = "release"
codegen-units = 1
[profile.p-abort] # 只加 panic=abort
inherits = "release"
panic = "abort"
[profile.p-strip] # 只加 strip
inherits = "release"
strip = "symbols"
[profile.p-full] # 四项全上
inherits = "release"
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "symbols"
[profile.p-nostrip] # 与 p-full 相同但 strip=none(用于隔离 strip)
inherits = "release"
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "none"
[profile.p-sizez] # 全套 + opt-level=z
inherits = "release"
opt-level = "z"
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "symbols"
[profile.p-sizez-dbg] # 全套 + opt-level=z + debug=1
inherits = "release"
opt-level = "z"
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "none"
debug = 1

被测代码是纯 std 的字符串/哈希表/格式化小程序(约 60 行),刻意包含格式化、HashMap、panic! 路径和 #[inline(never)] 函数,让各优化等级有可区分的产物。

构建与测量:

Terminal window
for p in release p-lto p-cgu p-abort p-strip p-full p-nostrip p-sizez p-sizez-dbg; do
cargo build --profile "$p"
stat -c '%s %n' "target/$p/size-bench.exe"
stat -c '%s %n' "target/$p/size_bench.pdb" 2>/dev/null
done

B2. 结果

profile exe(字节) exe(KiB) 相对默认 该档编译耗时 pdb(字节)
release 默认 156,672 153.0 — 0.49s 1,331,200
+ lto = "fat" 143,360 140.0 −8.5% 1.33s —
+ codegen-units = 1 155,136 151.5 −1.0% 0.54s —
+ panic = "abort" 151,040 147.5 −3.6% 0.44s —
+ strip = "symbols" 156,672 153.0 0.0% 0.45s —
四项全上(p-full) 135,168 132.0 −13.7% 1.39s 897,024
四项全上但 strip="none"(p-nostrip) 135,168 132.0 −13.7% 1.40s 897,024
全套 + opt-level = "z"(p-sizez) 128,000 125.0 −18.3% 1.26s 888,832
全套 + opt-level="z" + debug=1 128,512 125.5 −18.0% 1.30s 987,136

性价比排序:lto(−8.5%)> panic=abort(−3.6%)> codegen-units=1(−1.0%)> strip(0.0%)。再加上 opt-level="z" 才能把总收益推到 −18.3%。

代价用编译时间衡量:默认档 0.49s,上了 LTO 后 1.33~1.40s,2.7 倍。小 crate 上不明显,真实项目里这就是 A1 表里那 15 秒的差距。

B3. 重点:Windows 上 strip 是无效操作

这一项我做了最严格的对照——两个 profile 除 strip 外完全相同(都是 lto=fat, codegen-units=1, panic=abort):

配置 exe pdb
strip = "symbols"(p-full) 135,168 B 897,024 B
strip = "none"(p-nostrip) 135,168 B 897,024 B

字节数完全相同,连 .pdb 都没变。

原因:MSVC 目标下的调试信息与符号由 link.exe 决定,写进独立的 .pdb 文件,而 rustc 的 strip 走 LLVM objcopy 路径。面对 PE/COFF 产物,绝大多数符号本来就不在 exe 里,strip 没有东西可剥。

所以你不需要为 strip 反复调参。这条结论只适用于 *-pc-windows-msvc;Linux ELF 和 macOS Mach-O 上 strip 是真实有效的(符号表内嵌在可执行文件里),别把 Windows 的结论外推。

B4. .pdb 才是体积大头

产物 exe pdb 比例
小 crate,release 默认 153.0 KiB 1.27 MiB 8.5×
小 crate,四项全上 132.0 KiB 876 KiB 6.6×
小 crate,opt-level="z" 全套 125.0 KiB 868 KiB 6.9×
小 crate,debug=1 不 strip 125.5 KiB 964 KiB 7.7×
du-dust,默认 profile 2.75 MiB 1.50 MiB 0.55×

在 exe 上抠 8.5% 的同时,旁边躺着一个比它大 6~8 倍的 .pdb。发布时把 .pdb 单独归档、不随分发包发出去,收益远大于继续调优化等级。

顺带一个反直觉点:debug = 1 在这一档里几乎没让 exe 变大(125.5 vs 125.0 KiB,+0.4%),但 pdb 从 868 KiB 涨到 964 KiB。Windows 的调试信息进 pdb 不进 exe,所以“加调试信息会不会让发布体积变大”这个担心,在 MSVC 上基本不成立。

五、实验 C:Cargo 缓存勘察

Terminal window
du -sh ~/.cargo/registry/cache ~/.cargo/registry/src ~/.cargo/registry/index ~/.cargo/git ~/.cargo/bin
du -sh ~/.rustup
du -sh "$TEMP"/proc-macro-srv*
位置 实测大小 删掉的代价
项目 target/(本实验各档) 217 MB ~ 677 MB 每档 全量重编
~/.cargo/registry/src 642 MB 重新解压已下载的包
~/.cargo/registry/cache 281 MB 重新下载 .crate
~/.cargo/registry/index 73 MB 重新拉索引
~/.cargo/git 17 MB 重新 clone git 依赖
~/.cargo/bin 20 MB cargo install 装的东西全没
~/.rustup(stable + nightly) 3.2 GB 重新下载工具链(各 1.6 GB)
%TEMP%\proc-macro-srv* 794 MB / 10 份 无需代价,纯残留

各档 target 目录的实际占用(这也是为什么实验后要清理):

目录 大小
t-dev(dev profile) 677 MB
t-thin 442 MB
t-fast / t-raw 440 MB
t-base / t-lld 435 MB
t-check 217 MB

(cargo check 的目录只有 217 MB,不到完整 build 的一半。)

%TEMP% 下的 proc-macro-srv* 值得单独说:

23 MB proc-macro-srv5b44-0 172 MB proc-macro-srv74cc-0
111 MB proc-macro-srv754c-0 172 MB proc-macro-srv77e0-0
111 MB proc-macro-srv77e0-1 23 MB proc-macro-srv7dec-0
69 MB proc-macro-srv8410-0 23 MB proc-macro-srva90c-0
69 MB proc-macro-srvaf88-0 23 MB proc-macro-srvb998-0

共 10 个目录、约 794 MB。这是 rust-analyzer 为每个工具链解压的 proc-macro server 副本,进程退出后不清理,日积月累。确认没有 rust-analyzer 在跑后可以直接删:

Terminal window
rm -rf "$TEMP"/proc-macro-srv*

清理命令清单:

Terminal window
cargo clean # 清当前项目 target(通常是最大头)
cargo cache -a # cargo-cache 插件,清 registry 里的陈旧版本
cargo cache --autoclean # 自动清理
rm -rf ~/.cargo/registry/src # 清源码缓存,下次自动重新解压
rm -rf "$TEMP"/proc-macro-srv* # 794 MB 残留

另外这台机器没有 ~/.cargo/config.toml,全局配置为空。跨项目、跨机器的产物级缓存要靠 sccache(cargo install sccache + RUSTC_WRAPPER=sccache),这部分我没有实测,不写结论。

六、结论与证据索引

# 结论 支撑数据
1 日常用 dev 迭代,别用 release dev 改一行 1.19s vs release 改一行 18.27s
2 release 没有增量编译 release 无改动 18.17s,改一行 18.27s,差 0.1s
3 根因是 build script 每次重跑 + LTO 全量重链 -v 日志出现 Running ...build-script-build
4 cargo check 是第二便宜的手段 18.92s,比 release build 快 54.6%
5 lto 是编译时间的最大变量,也是体积的最大杠杆 关掉 LTO 省 36.4% 时间,代价 exe +9.1%
6 thin LTO 在此项目上双输 26.60s(≈关闭 LTO 的 26.48s),但体积大 3.4%
7 strip 在 Windows/MSVC 上完全无效 strip 开/关:exe 均 135,168 B,pdb 均 897,024 B
8 .pdb 比 exe 大 6~8 倍,应单独归档 小 crate:132 KiB exe / 876 KiB pdb
9 opt-level="z" 是体积的最后一档 −18.3%(全套基础上再降)
10 rust-lld 对单二进制项目零收益 42.35s vs 41.66s(噪声内)
11 单二进制项目用不到 lld 的原因:无链接阶段 74 个依赖全为 .rlib
12 nightly 并行前端真实收益 −23.2% 同版本配对:39.64s → 30.43s
13 -Zjobs-frontend=N 会直接构建失败 error: unknown unstable option: jobs-frontend
14 Cranelift 编译快 10.6%,产物 2.7 倍 23.68s / 8.01 MiB vs 26.48s / 3.00 MiB
15 panic="abort" 体积收益 3.6% 147.5 KiB vs 153.0 KiB
16 codegen-units=1 体积收益仅 1.0% 151.5 KiB vs 153.0 KiB
17 缓存占用最大的是 .rustup 与 registry/src 3.2 GB / 642 MB
18 %TEMP% 有 794 MB rust-analyzer 残留 10 个 proc-macro-srv* 目录
19 MSYS 下必须走 PATH 或 cmd.exe 执行产物 直接调用 rc=127,走 PATH 输出 Dust 1.2.6

七、局限

诚实地列出这份报告的外推边界:

  • 单机单项目。 只有一台 16 核机器、一个真实项目。各档的百分比与项目规模、依赖数量、单 crate 体量强相关,别把「−36.4%」「−23.2%」当成通用常数。
  • 每档 3 次,未做统计检验。 报的是中位数与极差,用来观察稳定性够用,但不构成显著性结论。极差在 0.5~1.35s,与部分配置间的差距(如 thin 26.60 vs lto=off 26.48)同量级——这几档之间的差异不能被判定为真实差异,我据实写出而不做强结论。
  • 跨版本比较已单独标注。 nightly 1.100 与 stable 1.98 的差异(约 −4.8%)混在“版本升级”里,只有同版本配对数据用于归因。
  • rust-lld 与 Cranelift 是单次测量。 这两项没做 3 次重复,报告里已注明,它们的差额(+1.7% 与 −10.6%)比重复测量的噪声更接近临界。
  • 体积实验用的是小 crate。 小 crate 的符号/对齐占比与大项目不同,绝对 KiB 数不可直接套用,但开关之间的相对贡献(lto ≫ abort > cgu > strip=0)具有参考价值。
  • strip 的结论仅限 MSVC。 ELF/Mach-O 上结论会不同。
  • 未测项:sccache、-Zbuild-std、mold 链接器(Linux)、debug = "line-tables-only"、增量编译在 release 下强制开启(incremental = true)的效果。这些都可能显著改变结论,本报告不涉及。

八、复现脚本

#!/usr/bin/env bash
# 复现本报告的编译速度实验(需要网络或用 --offline + 本地 registry 缓存)
set -u
export CARGO_TERM_COLOR=never
# 1. 准备源码副本
cd /path/to/workdir
cp -r ~/.cargo/registry/src/index.crates.io-*/du-dust-1.2.6 dust2
cd dust2 && rm -f Cargo.lock
# 2. 工具函数:从 Cargo 输出里取耗时(Cargo 输出 "Finished ... in X.XXs")
tm() { grep -o "in [0-9.]*s" /tmp/r.log | sed -n '$p' | grep -o "[0-9.]*"; }
# 3. 单档测量:删掉 target 目录保证冷编译
one() {
local label="$1" tdir="$2"; shift 2
rm -rf "$tdir"
"$@" > /tmp/r.log 2>&1
local rc=$?
local secs; secs=$(tm)
local exe; exe=$(stat -c %s "$tdir/release/dust.exe" 2>/dev/null || echo 0)
echo "$label | ${secs}s | rc=$rc | exe=${exe}B"
}
# 4. 五个配置 × 3 次
for i in 1 2 3; do
echo "=== round $i ==="
one "default " tA cargo build --release --offline --target-dir tA
one "lto=off cgu16 " tB cargo build --release --offline --target-dir tB \
--config profile.release.lto=false --config profile.release.codegen-units=16
one "thin cgu16 " tC cargo build --release --offline --target-dir tC \
--config 'profile.release.lto="thin"' --config profile.release.codegen-units=16
one "nightly thr16 " tD env RUSTFLAGS=-Zthreads=16 cargo +nightly build --release --offline --target-dir tD
one "cargo check " tE cargo check --release --offline --target-dir tE
done
# 5. 热编译 vs 改一行
for i in 1 2 3; do one "release warm #$i" tA cargo build --release --offline --target-dir tA; done
for i in 1 2 3; do touch src/main.rs; one "release touch #$i" tA cargo build --release --offline --target-dir tA; done
# 6. dev 侧对照
rm -rf tDev
one "dev cold" tDev cargo build --offline --target-dir tDev
for i in 1 2 3; do one "dev warm #$i" tDev cargo build --offline --target-dir tDev; done
for i in 1 2 3; do touch src/main.rs; one "dev touch #$i" tDev cargo build --offline --target-dir tDev; done

九、可以直接抄的配置

~/.cargo/config.toml:

[target.x86_64-pc-windows-msvc]
# 实测对单二进制项目无收益(42.35s vs 41.66s),
# 多 bin / 大量测试目标的项目才值得开
# linker = "rust-lld"
[net]
retry = 3
git-fetch-with-cli = true

项目 Cargo.toml:

[profile.dev]
debug = 1 # 默认 2;降到 1 能减小 target/ 并加快链接,断点仍可用
[profile.release]
opt-level = "z" # 体积优先(实测 −18.3%);要运行速度就写 3
lto = "fat" # 追求最小体积。想省编译时间就用 false(−36.4%),
# 不建议 "thin":本项目上它与 false 打平但产物更大
codegen-units = 1
panic = "abort" # −3.6%;会禁用 catch_unwind,测试 profile 别继承
# strip = "symbols" # Windows 上无效;Linux/macOS 上保留
[profile.release.package."*"]
opt-level = 2 # 依赖不必做极致优化,省下大量编译时间

仅 nightly 可用的一条(实测 −23.2%):

[build]
rustflags = ["-Zthreads=16"] # stable 上会直接报错,别写进全局配置

panic = "abort" 会改变 cargo test 的 panic 行为(无法捕获展开),若测试也走 release profile,务必确认后再开。