为什么Rust的可执行文件通常比C 的要大?
我编译了这个Rust文件 simple_rust.rs
#![no_std]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
使用 rustc simple_rust.rs -C opt-level="z" --crate-type lib,rustc版本1.94.0,得到文件 libsimple_rust.rlib
我随后编译了这个C 文件 simple_c.c
int add(int a, int b) {
return a+b;
}
使用 gcc -c simple_c.c,gcc版本15.01,得到文件 simple_c.o
我注意到 libsimple_rust.rlib 的大小是5248字节,simple_c.o 的大小是1288字节(两者均在Ubuntu Linux下编译)。因此Rust二进制文件的大小大约是C 二进制文件的4 倍。
请注意,这个问题 的答案主要与Rust标准库默认被静态链接到Rust二进制文件中有关,与C 编译器+ 链接器处理库的方式不同。不过据我所知,上述示例中并没有链接任何库。尽管如此,我在这里看到的资料是,Rust的 *.rlib文件不仅包含目标代码,还包含LLVM位码、元数据和符号表。
因此我猜这些附加内容在二进制文件体积偏大方面起了主要作用,但也许我还漏掉了什么。
解决方案
在以上所有评论的帮助下,我找到了自己的答案。
简短的回答是:Rust二进制文件其实并不比C 二进制文件大。
(至少在一般情况下并非如此,在我修订后的示例中,Rust二进制实际上更小。)
长回答如下:在我使用 ar 与 objdump 工具仔细查看Rust *.rlib 二进制后,发现其中包含一个元数据文件(*.rmeta)和一个对象文件(*.o)。令人惊讶的是,那个对象文件并不包含任何代码,因此add函数似乎被编译进了元数据文件。事实上,Rust的元数据文件可以包含所谓的MIR代码,见 此处。
我随后把这个文件
#![no_std]
pub fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
作为输入交给rustc,在 *.rlib 文件中找到了一个大小为1184字节的对象文件,其中实际包含了我的函数代码的文本段。
最后为了对比,我还编译了这个C 文件:
unsigned long long fibonacci(unsigned int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
得到的对象文件大小为1480字节。此外,仅查看两个对象文件的文本段,我发现从Rust源代码生成的机器代码比C 编译器生成的少12字节。