问题:
Why are Rust executables so huge?
为什么Rust可执行文件如此巨大?
Just having found Rust and having read the first two chapters of the documentation, I find the approach and the way they defined the language particularly interesting. So I decided to get my fingers wet and started out with Hello world...
我刚刚发现Rust,并且阅读了文档的前两个章节,发现他们定义语言的方法和方式特别有趣的。所以我决定动动我的手指,开始敲Hello world...
I did so on Windows 7 x64, btw.
顺便说一句,我是在Windows 7 x64系统上这么做的。
fn main() {
println!("Hello, world!");
}
Issuing cargo build and looking at the result in targets\debug I found the resulting .exe being 3MB. After some searching (documentation of cargo command line flags is hard to find...) I found --release option and created the release build. To my surprise, the .exe size has only become smaller by an insignificant amount: 2.99MB instead of 3MB.
问题是在cargo build,去看targets\debug下的结果,我发现结果.exe文件有3MB.查找了一些之后(很难找到cargo命令行标记的文档...),我发现 --release 选项,并且创建release构建。令我惊讶的是,.exe文件大小仅仅变小了点儿,从3MB替换为2.99MB。
So, confessing I am a newbie to Rust and its ecosystem, my expectation would have been that a Systems Programming language would produce something compact.
所以,我承认对于Rust和它的生态是一个新手,我的期望是作为一个系统编程语言,它应该生成一些紧凑的东西。
Can anyone elaborate on what Rust is compiling to, how it can be possible it produces such huge images from a 3 liner program? Is it compiling to a virtual machine? Is there a strip command I missed (debug info inside the release build?)? Anything else which might allow to understand what is going on?
有人能详细说明一下Rust编译的是什么,它怎么可能从一段3行程序中产生如此巨大的镜像?它是编译到一个虚拟机么?是有一个我疏忽的瘦身命令(在release构建中有调试信息?)么?还是有其它的能让我明白发生了什么的原因?
评论:
I think 3Mb contains not only Hello World, but also all the needed environment for the platform. The same thing can be seen with Qt. That does not mean if you write a 6-line program the size will become 6 Mb. It will stay at 3Mb and will grow very slowly after that.
我认为3Mb不仅包含了Hello World,而且也包含了平台全部所需要的环境。Qt也有同样的处理。那不意味着如果你写一个6行的程序,文件大小就会变成6Mb.它将保持在3Mb,并且之后增长将非常缓慢。
@AndreiNikolaenko I am aware of that. But this hints that either they do not handle libraries as C does, adding only what is required to an image or that something else is going on.
@AndreiNikolaenko 我明白。但是这表明,要么他们不能像C那样处理库,只能向镜像添加所需要的,要么他们正在做一些其他事情。
@user2225104 See my answer, RUST handles libraries in the same (or similar) way as C does, but by default C does not compile static libraries into your program (at least, on C++).
@user2225104 看我的答案,RUST处理库和C是相同(或相似)的,但是默认C是不编译静态库进入程序的(至少在C++中)。
Very related: Do DLLs built with Rust require libgcc.dll on run time?.
非常相关:Rust构建的DLL在运行时需要libgcc.dll么?
Is this outdated now? With rustc version 1.35.0 and no cli options I get an exe that is 137kb in size. Does it automatically compile dynamically linked now or did something else happen in the meantime?
这个现在是过时了么?在rustc 1.35.0版本下,没有命令行参数,我得到一个exe文件是137kb。现在它是自动编译动态链接还是与此同时发生了其他事情?
回答1(selected):
Rust uses static linking to compile its programs, meaning that all libraries required by even the simplest Hello world! program will be compiled into your executable. This also includes the Rust runtime.
Rust使用静态链接来编译程序,意思是即使是最简单的Hello world!程序也需要将所有库编译进你的可执行文件。这也包含了Rust运行时情况。
To force Rust to dynamically link programs, use the command-line arguments -C prefer-dynamic; this will result in a much smaller file size but will also require the Rust libraries (including its runtime) to be available to your program at runtime. This essentially means you will need to provide them if the computer does not have them, taking up more space than your original statically linked program takes up.
为了强制Rust动态链接程序,使用命令行参数 -C prefer-dynamic;这将导致文件大小变小了很多,但是这也要求在运行时你的程序Rust库(包括库的运行时)是可用的。这本质上意味着如果电脑中没有它们,你将需要提供它们,它们比你原来的静态链接程序占用更多空间。
For portability I'd recommend you statically link the Rust libraries and runtime in the way you have been doing if you were to ever distribute your programs to others.
从可移植性考虑,我建议你静态链接Rust库和运行时,依照此方法,如果你曾经分发你的程序给其他人,你是可以工作的。
评论:
@user2225104 Unsure about Cargo, but according to this bug report on GitHub, this isn't yet possible unfortunately.
@user2225104 关于Cargo还不确定,但是不幸的是,根据在GitHub上的这个bug记录,这还不可能。
I don't think static linking explains the huge HELLO-WORLD. Shouldn't it only link in the parts of the libraries that are actually used, and HELLO-WORLD uses virtually nothing?
我认为静态链接不能解释HELLO-WORLD巨大的原因。它不是应该仅仅链接实际用到一部分库,而HELLO-WORLD几乎什么都没有用?
BitTickler cargo rustc [--debug or --release] -- -C prefer-dynamic
备忘:cargo rustc [--debug or --release] -- -C prefer-dynamic
@daboross Thank you very much. I have been tracking this related RFC. It's really a pity since Rust also targets system programming.
@daboross 非常感谢。我已经在跟踪相关的RFC。很遗憾,因为Rust还把系统编程作为目标。
@Nulik: Yes, by default, but that's because Rust defaults to static builds (all dependencies, including runtime, included), while Go links its runtime dynamically. On my CentOS 7 system, Go's helloworld compiles to ~76K, but on top of standard stuff, it takes a runtime dynamic dependency on libgo.so, which is over 47M. The default Rust helloworld (as made with cargo new) doesn't have any unique dynamic dependencies, holding everything but basic C runtime stuff in a 1.6M executable; with tweaks (optimize for size, using LTO, aborting on panic), it drops to 0.6M.
@Nulik: 是的,默认情况下是这样,但是那是因为Rust默认是静态编译(所有依赖,包含运行时,已经包含的),而Go是动态的链接运行时的。在我的CentOS 7 系统上,Go的helloworld 编译为76K,但是在标准的事务上面,运行时需要动态的依赖libgo.so,它超过了47M。Rust的helloworld(像用cargo new生成的)默认没有任何独特的动态依赖,除了基于C的运行时内容,其他所有内容都包含在一个1.6M的可执行文件中;做一些调整(优化大小,使用LTO, 终止painc),它下降到0.6M。
回答2:
I don't have any Windows systems to try on, but on Linux, a statically compiled Rust hello world is actually smaller than the equivalent C. If you are seeing a huge difference in size, it is probably because you are linking the Rust executable statically and the C one dynamically.
我没有任何windows系统去尝试,但是在Linux,静态编译Rust hello wolrd实际上比等效的C更小。如果你看到大小有巨大的差异,那可能是因为你静态的链接了Rust可执行文件,而C是动态的。
With dynamic linking, you need to take the size of all the dynamic libraries into account too, not just the executable.
对于动态链接,你也需要将所有的动态库的大小统计进去,而不仅仅是可执行文件。
So, if you want to compare apples to apples, you need to make sure either both are dynamic or both are static. Different compilers will have different defaults, so you can't just rely on the compiler defaults to produce the same result.
所以,如果你想一一进行比较,你需要确保要么两者都是动态的,要么两者都是静态的。不同的编译器会有不同的默认值,所以你不能仅仅依靠编译器默认值来产生相同的结果。
If you're interested, here are my results:
如果你有兴趣,这里是我的结果:
-rw-r--r-- 1 aij aij 63 Apr 5 14:26 printf.c
-rwxr-xr-x 1 aij aij 6696 Apr 5 14:27 printf.dyn
-rwxr-xr-x 1 aij aij 829344 Apr 5 14:27 printf.static
-rw-r--r-- 1 aij aij 59 Apr 5 14:26 puts.c
-rwxr-xr-x 1 aij aij 6696 Apr 5 14:27 puts.dyn
-rwxr-xr-x 1 aij aij 829344 Apr 5 14:27 puts.static
-rwxr-xr-x 1 aij aij 8712 Apr 5 14:28 rust.dyn
-rw-r--r-- 1 aij aij 46 Apr 5 14:09 rust.rs
-rwxr-xr-x 1 aij aij 661496 Apr 5 14:28 rust.static
These were compiled with gcc (Debian 4.9.2-10) 4.9.2 and rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (built 2015-04-03), both with default options and with -static for gcc and -C prefer-dynamic for rustc.
这些使用gcc (Debian 4.9.2-10) 4.9.2 和 rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (built 2015-04-03) 编译,两者都是默认选项,并且对于 gcc 使用 -static ,对于rustc 使用-C prefer-dynamic。
I had two versions of the C hello world because I thought using puts() might link in fewer compilation units.
我有两个版本的C hello world,因为我本来想使用 puts() 可能链接编译单元会少一些。
If you want to try reproducing it on Windows, here are the sources I used:
如果你想在windows尝试重新生成,这里有我使用的源代码:
printf.c:
#include
int main() {
printf("Hello, world!\n");
}
puts.c:
#include
int main() {
puts("Hello, world!");
}
rust.rs
fn main() {
println!("Hello, world!");
}
Also, keep in mind that different amounts of debugging information, or different optimization levels would also make a difference. But I expect if you are seeing a huge difference it is due to static vs. dynamic linking.
还有,请记住,不同的大量的调试信息,或者不同的优化等级也会产生不同。但是我认为如果看到巨大的差异,那是由于静态和动态链接原因。
评论:
gcc is smart enough to do exactly the printf -> puts substitution itself, that's why results are identical.
gcc 足够智能,准确的将printf替换为puts,这就是为什么结果是相同的。
As of 2018 if you want a fair comparison do remember to "strip" the executables, as a hello world Rust executable on my system is a whopping 5.3MB but drops down to less than 10% of that when you remove all the debug symbols and such.
截止2018年,如果你想有一个公平的比较,记得给可执行文件瘦身,因为一个hello world Rust可执行文件,在我的系统中有5.3MB那么大,但是当你移除所有调试符号等,会下降到10%以下。
@MattiVirkkunen: Still the case in 2020; the natural size seems smaller (nowhere near 5.3M), but the ratio of symbols to code is still pretty extreme. The debug build, purely default options on Rust 1.34.0 on CentOS 7, stripped with strip -s, drops from 1.6M to 190K. The release build (defaults plus opt-level='s', lto = true, and panic = 'abort' to minimize size) drops from 623K to 158K.
@MattiVirkkunen: 到2020年,情况依然如此;自然大小似乎小一些(接近5.3M),但是符号与代码的比例仍然相当极端。在CentOS 7 上进行调式编译,Rust 1.34.0版本, 完全默认选项,瘦身采用 strip -s,结果从1.6M下降到190K。发布编译(默认值 加上 opt-level='s', lto = true, 和 panic = 'abort' 参数,使大小最小),从623K下降到158K。