编译慢,先要知道慢在哪里。这篇把 cargo 的 --timings 报告拆开,找出关键路径;再用 rustc 的 -Z time-passes 看各阶段;最后量几个常见旋钮(并行度、泛型、debuginfo、优化级别、增量)到底有多大效果。
src/main.rs:
1 | use serde::{Serialize, Deserialize};
2 | #[derive(Serialize, Deserialize, Debug)]
3 | struct P { a: i32, b: String }
4 | fn main() {
5 | let p = P { a: 1, b: "x".into() };
6 | let s = serde_json::to_string(&p).unwrap();
7 | let re = regex::Regex::new(r"\d+").unwrap();
8 | println!("{} {}", s, re.is_match("a1"));
9 | }
Cargo.toml 依赖: serde = { version = "1", features = ["derive"] }; serde_json = "1"; regex = "1"
cargo build --timings:退出码 0;整体 5.1 秒;最后一行:Finished `dev` profile [unoptimized + debuginfo] target(s) in 5.05s
HTML 报告大小 47 KiB,共 28 个编译单元(build script 的编译、运行与库各算一个)
单元 类型 开始(s) 耗时(s) 结束(s)
serde_core 1.0.229 build script 1.89 0.20 2.09
itoa 1.0.18 lib 1.89 0.11 2.00
memchr 2.8.3 lib 1.89 0.30 2.19
proc-macro2 1.0.107 build script 1.89 0.22 2.11
zmij 1.0.23 build script 1.89 0.20 2.09
quote 1.0.47 build script 1.89 0.19 2.08
unicode-ident 1.0.26 lib 1.89 0.07 1.96
regex-syntax 0.8.11 lib 1.89 1.11 3.00
serde_json 1.0.151 build script 1.89 0.17 2.06
serde 1.0.229 build script 1.89 0.19 2.08
serde_json 1.0.151 build-script (run) 2.06 0.09 2.15
quote 1.0.47 build-script (run) 2.08 0.13 2.21
zmij 1.0.23 build-script (run) 2.09 0.13 2.22
serde 1.0.229 build-script (run) 2.09 0.13 2.22
serde_core 1.0.229 build-script (run) 2.09 0.13 2.22
proc-macro2 1.0.107 build-script (run) 2.11 0.25 2.36
aho-corasick 1.1.5 lib 2.12 0.68 2.80
serde_core 1.0.229 lib 2.22 1.06 3.28
zmij 1.0.23 lib 2.22 0.17 2.39
proc-macro2 1.0.107 lib 2.36 0.24 2.60
quote 1.0.47 lib 2.54 0.16 2.70
syn 3.0.6 lib 2.65 0.84 3.49
regex-automata 0.4.18 lib 2.73 1.32 4.05
serde_json 1.0.151 lib 3.22 0.52 3.74
serde_derive 1.0.229 lib 3.49 0.98 4.47
regex 1.13.1 lib 3.67 0.24 3.91
serde 1.0.229 lib 4.47 0.39 4.86
u167p 0.1.0 u167p "bin" 4.86 0.19 5.05
最后一个单元结束于 5.05 秒;同一时刻最多并行 10 个单元;单元耗时总和 10.41 秒
有分段信息的单元里:frontend 合计 5.59 秒、codegen 合计 1.62 秒
最后开始的单元是 u167p(开始于 4.86 秒):它要等所有依赖的 rmeta 就绪
直接解锁 u167p 的单元:['serde']增量编译的原理见增量编译,构建配置见Cargo profile。
-j 1 3 次:11.8, 11.7, 11.9 秒;中位数 11.8 秒 默认 3 次:3.2, 3.2, 3.2 秒;中位数 3.2 秒
退出码 0;共 39 个阶段(另有 total = 0.161 秒;link / link_crate / link_binary 分别为 0.047/0.047/0.047 秒);耗时前 10: 阶段 秒 finish_ongoing_codegen 0.047 LLVM_passes 0.047 run_linker 0.040 MIR_borrow_checking 0.019 type_check_crate 0.016 codegen_crate 0.014 coherence_checking 0.008 monomorphization_collector_graph_walk 0.007 get_apple_sdk_root 0.006 codegen_to_LLVM_IR 0.005
各编译阶段的含义见Rust 编译的几个阶段。
N 泛型 (秒) dyn (秒) 泛型单态化项数 dyn 单态化项数
50 0.10 0.10 50 1
200 0.15 0.13 200 1
800 0.35 0.29 800 1泛型单态化的细节见单态化与代码生成。
配置 耗时(秒) 可执行文件(KiB) dev(默认,debuginfo=2) 3.3 5869 dev + debug=0 3.3 4948 dev + opt-level=1 5.1 2781 release 4.8 2140
干净构建 3.6 秒 没有任何改动再构建:0.03 秒 改 main.rs 里的一个字符串:0.20 秒,重编 1 个包 给 serde_json 加 preserve_order 特征:2.63 秒,重编 5 个包:serde_json, hashbrown, equivalent, indexmap, u167p