繁体   English   中英

为什么 Rust 可执行文件如此庞大?

[英]Why are Rust executables so huge?

刚刚找到 Rust 并阅读了文档的前两章,我发现他们定义语言的方法和方式特别有趣。 所以我决定亲自动手,从“Hello, world.”开始。

顺便说一句,我是在 Windows 7 x64 上这样做的。

fn main() {
    println!("Hello, world!");
}

发布cargo build并在targets\debug中查看结果,我发现生成的.exe为 3MB。 经过一些搜索(cargo 命令行标志的文档很难找到......)我找到了--release选项并创建了发布版本。 令我惊讶的是,.exe 的大小只变小了一个微不足道的量:2.99MB 而不是 3MB。

因此,承认我是 Rust 及其生态系统的新手,我的期望是系统编程语言会产生一些紧凑的东西。

任何人都可以详细说明 Rust 正在编译什么,它怎么可能从 3 行程序生成如此巨大的图像? 它是编译到虚拟机吗? 是否有我错过的 strip 命令(发布版本中的调试信息?)? 还有什么可以让我们理解正在发生的事情吗?

Rust 使用静态链接来编译它的程序,这意味着即使是最简单的Hello world! 程序将被编译成您的可执行文件。 这也包括 Rust 运行时。

要强制 Rust 动态链接程序,请使用命令行参数-C prefer-dynamic 这将导致文件大小小得多,也需要 Rust 库(包括其运行时)在运行时可供您的程序使用。 这基本上意味着你将需要为他们提供如果计算机没有他们,占用了比你原来的静态链接程序占用更多的空间。

为了可移植性,如果您要将程序分发给其他人,我建议您按照您一直在做的方式静态链接 Rust 库和运行时。

我没有任何 Windows 系统可以尝试,但在 Linux 上,静态编译的 Rust hello world 实际上比等效的 C 小。如果您看到大小差异很大,那可能是因为您正在链接 Rust 可执行文件静态和 C 一个动态。

使用动态链接,您还需要考虑所有动态库的大小,而不仅仅是可执行文件。

因此,如果您想将苹果与苹果进行比较,您需要确保两者都是动态的,或者都是静态的。 不同的编译器会有不同的默认值,所以你不能仅仅依赖编译器的默认值来产生相同的结果。

如果你有兴趣,这是我的结果:

-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

这些是用 gcc (Debian 4.9.2-10) 4.9.2 和 rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (built 2015-04-03) 编译的,都有默认选项和-static for gcc和-C prefer-dynamic rustc -C prefer-dynamic

我有两个版本的 C hello world,因为我认为使用puts()可能会链接更少的编译单元。

如果你想尝试在 Windows 上重现它,这里是我使用的来源:

printf.c:

#include <stdio.h>
int main() {
  printf("Hello, world!\n");
}

puts.c:

#include <stdio.h>
int main() {
  puts("Hello, world!");
}

rust.rs

fn main() {
    println!("Hello, world!");
}

此外,请记住,不同数量的调试信息或不同的优化级别也会有所不同。 但是我希望如果您看到巨大的差异,那是由于静态链接与动态链接。

有关减小 Rust 二进制文件大小的所有方法的概述,请参阅min-sized-rust存储库。

当前减少二进制大小的高级步骤是:

  1. 使用 Rust 1.32.0 或更新版本(默认情况下不包括jemalloc
  2. 将以下内容添加到Cargo.toml
[profile.release]
opt-level = 'z'     # Optimize for size.
lto = true          # Enable Link Time Optimization
codegen-units = 1   # Reduce number of codegen units to increase optimizations.
panic = 'abort'     # Abort on panic
  1. 使用cargo build --release在发布模式下cargo build --release
  2. 在生成的二进制文件上运行strip

使用nightly Rust 可以做更多事情,但我会将这些信息留在min-sized-rust因为它会随着时间的推移而变化,因为使用了不稳定的功能。

您还可以使用#![no_std]删除 Rust 的libstd 有关详细信息,请参阅min-sized-rust

使用 Cargo 编译时,可以使用动态链接:

cargo rustc --release -- -C prefer-dynamic

这将大大减少二进制文件的大小,因为它现在是动态链接的。

至少在 Linux 上,您还可以使用strip命令剥离二进制符号:

strip target/release/<binary>

这将使大多数二进制文件的大小大约减半。

每晚安装rust - rustup toolchain install nightlyrustup default nightly rustup toolchain install nightly

现在,在项目中的所有Cargo.toml 文件中进行这些更改。

在Cargo.toml顶部的[package]之前添加cargo-features = ["strip"]

在底部,或者在[dependencies][package]添加,

[profile.release]
# strip = true  # Automatically strip symbols from the binary.
opt-level = "z"  # Optimize for size.
lto = true  # Enable link time optimization
codegen-units = 1  # Reduce parallel code generation units

现在使用RUSTFLAGS='-C link-arg=-s' cargo build --release

我发现这些链接很有用 - https://collabora.com/news-and-blog/blog/2020/04/28/reducing-size-rust-gstreamer-plugin/https://github.com/johnthagen/min -size-rusthttps://arusahni.net/blog/2020/03/optimizing-rust-binary-size.html

可执行文件可能很大的原因之一是 Rust 编译器处理泛型的方式。 Rust 不使用多态性,而是使用单态化 这意味着编译器为每个具体类型的每个泛型函数创建一个单独的代码副本(根据需要)。开发指南在这里概述了它

例如,如果我使用像Vec这样的泛型类型,并且在我的代码中有多个实例,如Vec<i32>Vec<bool>Vec<String> ,那么编译器将为上述泛型生成生成的代码的 3 个单独副本。 一个用于Vec<i32> ,一个用于Vec<bool> ,等等......

这种方法的缺点之一是编译时间增加并且二进制文件更大。 从好的方面来说,编译后的代码应该运行得更快,因为在运行时不需要推断任何东西,并且每个具体类型都有(应该有?)针对该具体类型的优化实现。

刚刚找到 Rust 并阅读了文档的前两章,我发现他们定义语言的方法和方式特别有趣。 所以我决定弄湿我的手指并开始使用 Hello world ...

我是在 Windows 7 x64 上这样做的,顺便说一句。

fn main() {
    println!("Hello, world!");
}

发出cargo build并查看targets\\debug中的结果,我发现生成的.exe为 3MB。 经过一番搜索(很难找到有关货物命令行标志的文档...),我找到了--release选项并创建了发布版本。 令我惊讶的是,.exe 的大小只变小了一点点:2.99MB 而不是 3MB。

所以,承认我是 Rust 及其生态系统的新手,我的期望是系统编程语言会产生一些紧凑的东西。

任何人都可以详细说明 Rust 正在编译的内容,它如何从 3 行程序生成如此巨大的图像? 是编译成虚拟机吗? 是否有我错过的 strip 命令(发布版本中的调试信息?)? 还有什么可能让您了解正在发生的事情吗?

这是一个功能,而不是一个错误!

您可以指定程序中使用的库版本(在项目关联的 Cargo.toml 文件中)(甚至是隐式版本)以确保库版本兼容性。 另一方面,这需要将特定库静态链接到可执行文件,从而生成大型运行时映像。

嘿,现在已经不是 1978 年了 - 许多人的计算机内存超过 2 MB :-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM