二进制为什么这么大?先要知道大在哪里。这篇用 size/nm 看文件的构成,用 cargo-bloat 按 crate 与函数归因,用 cargo-llvm-lines 看生成的 IR,再量依赖特征与编译选项各能省多少。
src/main.rs:
1 | use serde::{Deserialize, Serialize};
2 | #[derive(Serialize, Deserialize, Debug)]
3 | struct P { a: i32, b: String }
4 | fn main() {
5 | let arg = std::env::args().nth(1).unwrap_or_default();
6 | let p: P = serde_json::from_str(&arg).unwrap_or(P { a: 0, b: String::new() });
7 | let re = regex::Regex::new(&arg).unwrap();
8 | println!("{} {}", serde_json::to_string(&p).unwrap(), re.is_match("abc123"));
9 | }
hello(只有 println!):443 KiB;本程序:2240 KiB(约 5.1 倍)
size -m(Mach-O 各段):
Segment __PAGEZERO: 4294967296 (zero fill)
Segment __TEXT: 1458176
Section __text: 923880
Section __stubs: 876
Section __stub_helper: 900
Section __gcc_except_tab: 29952
Section __const: 312632
Section __cstring: 37406
Section __unwind_info: 21712
Section __eh_frame: 125028
Segment __DATA_CONST: 245760
Section __const: 231024
Section __got: 16
Segment __DATA: 16384
Section __la_symbol_ptr: 584
Section __data: 2656
nm 看到的符号数:6362
用 strip 命令再剥一次:2240 KiB → 1751 KiB
剥完后 nm 的符号数:76(nm 的提示:)LTO 与体积的关系见LTO 与二进制体积,构建配置见Cargo profile。
退出码 0
File .text Size Crate
12.1% 31.2% 281.1KiB regex_automata
11.8% 30.3% 273.5KiB std
8.8% 22.7% 205.2KiB regex_syntax
4.3% 11.1% 100.6KiB aho_corasick
1.0% 2.5% 22.1KiB serde_json
0.3% 0.7% 6.2KiB proc_macro2
0.3% 0.7% 6.2KiB memchr
0.1% 0.3% 2.9KiB regex
0.1% 0.3% 2.9KiB [Unknown]
0.1% 0.2% 1.9KiB u168p
0.1% 0.2% 1.4KiB serde_core
0.0% 0.1% 1.0KiB zmij
0.0% 0.1% 1.1KiB And 3 more crates. Use -n N to show more.
38.9% 100.0% 902.2KiB .text section size, the file size is 2.3MiB
Note: numbers above are a result of guesswork. They are not 100% correct and never will be. File .text Size Crate Name
0.4% 1.0% 9.3KiB std std::backtrace_rs::symbolize::gimli::resolve
0.4% 1.0% 8.9KiB regex_automata regex_automata::meta::strategy::new
0.4% 0.9% 8.5KiB regex_automata regex_automata::hybrid::search::find_fwd
0.4% 0.9% 8.4KiB regex_syntax <regex_syntax::hir::translate::TranslatorI as regex_syntax::ast::visitor::Visitor>::visit_post
0.4% 0.9% 8.3KiB regex_automata <regex_automata::nfa::thompson::pikevm::PikeVM>::search_imp
0.4% 0.9% 8.3KiB std <std::backtrace_rs::symbolize::gimli::Context>::new
0.3% 0.9% 7.7KiB regex_automata <regex_automata::nfa::thompson::pikevm::PikeVM>::which_overlapping_imp
0.3% 0.8% 6.8KiB regex_automata <regex_automata::nfa::thompson::compiler::Compiler>::c
0.3% 0.7% 6.6KiB std <gimli::read::dwarf::Unit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, usize>>::new
0.3% 0.7% 6.1KiB aho_corasick <aho_corasick::dfa::Builder>::build_from_noncontiguous
35.7% 91.7% 827.2KiB And 2300 smaller methods. Use -n N to show more.
38.9% 100.0% 902.2KiB .text section size, the file size is 2.3MiB regex 特征 release 大小(KiB)
regex 默认特征 2240
std + perf(无 unicode) 1597
std(无 unicode、无 perf) 1081
无 unicode 的版本用 [a-c]+1 运行:退出码 0,输出 {"a":0,"b":""} true
同一个程序用 \w+ 运行(Unicode 模式的 \w 需要 unicode 特征):退出码 101
thread 'main' (8225971) panicked at src/main.rs:7:38: called `Result::unwrap()` on an `Err` value: Syntax( ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ regex parse e特征开关的机制见Cargo 特征开关。
配置 大小(KiB) 相对上一行
release 默认 2240
+ strip = true 1751 -22%
+ panic = "abort" 1557 -11%
+ opt-level = "z" 1497 -4%
+ lto = true 1090 -27%
+ codegen-units = 1 1090 -0%
最终的最小版本照常运行(参数 [a-c]+1):退出码 0,输出 {"a":0,"b":""} true 退出码 0;前 12 行:
Lines Copies Function name
----- ------ -------------
13348 406 (TOTAL)
565 (4.2%, 4.2%) 1 (0.2%, 0.2%) <serde_json[99b760cc00e9b90]::de::Deserializer<serde_json[99b760cc00e9b90]::read::StrRead>>::ignore_value
396 (3.0%, 7.2%) 1 (0.2%, 0.5%) <&mut serde_json[99b760cc00e9b90]::de::Deserializer<serde_json[99b760cc00e9b90]::read::StrRead> as serde_core[e0e85677c1f3a27b]:
341 (2.6%, 9.8%) 1 (0.2%, 0.7%) <<u168p[af1d42ee5f8f31f5]::P as serde_core[e0e85677c1f3a27b]::de::Deserialize>::deserialize::__Visitor as serde_core[e0e85677c1f
308 (2.3%, 12.1%) 1 (0.2%, 1.0%) <serde_json[99b760cc00e9b90]::de::Deserializer<serde_json[99b760cc00e9b90]::read::StrRead>>::ignore_integer
301 (2.3%, 14.3%) 1 (0.2%, 1.2%) <serde_json[99b760cc00e9b90]::de::Deserializer<serde_json[99b760cc00e9b90]::read::StrRead>>::peek_invalid_type
269 (2.0%, 16.3%) 1 (0.2%, 1.5%) <serde_json[99b760cc00e9b90]::de::Deserializer<serde_json[99b760cc00e9b90]::read::StrRead>>::parse_integer
261 (2.0%, 18.3%) 1 (0.2%, 1.7%) u168p[af1d42ee5f8f31f5]::main
258 (1.9%, 20.2%) 1 (0.2%, 2.0%) <serde_json[99b760cc00e9b90]::de::Deserializer<serde_json[99b760cc00e9b90]::read::StrRead>>::parse_exponent
227 (1.7%, 21.9%) 1 (0.2%, 2.2%) <serde_json[99b760cc00e9b90]::de::Deserializer<serde_json[99b760cc00e9b90]::read::StrRead>>::parse_decimal编译时间的分析见编译时间分析。