第一次接触Rust,遍对其是否能代替C/C++产生了疑问,运行速度上很难依靠人去感知,但是输出文件的大小是可以看到的。

Rust输出的二进制文件要比C输出的大的多,据了解,大概和以下原因有关:

静态链接:

  • 默认情况下,Rust 产生的二进制是静态链接的,这意味着所有用到的库和函数都被直接包含在生成的文件中,而不是在运行时动态地加载。
  • 这一策略有其好处:生成的二进制文件在不同的系统上更具有独立性和可移植性,因为它不依赖于外部的库文件。但这也导致了较大的文件大小。
  • 即便是最简单的Rust程序,也会链接到Rust的标准库,也被称为std。这个库提供了许多基础的功能,如IO操作、线程管理、数据结构等。
  • 这些功能虽然在“Hello, World!”程序中可能未直接使用,但它们被包括在了编译的输出中。

其实 Rust的编译器(特别是其链接器)实际上是智能的,并采用了一个称为“树摇(tree shaking)”或“死代码消除(dead code elimination)”的过程,让它只会链接那些你的程序真正用到的库代码部分。

死代码消除:

  • Rust编译器会分析代码,确定哪些函数、变量和其他结构是未使用的。在编译和链接的过程中,所有未使用的代码(死代码)都不会出现在最终的二进制文件中。
  • Rust的标准库是模块化的。当你使用某个特定的模块或功能时,只有那部分代码会被拉入最终的二进制。例如,如果你的代码从标准库中只使用了 Vec 和 println!,那么只有与这些功能相关的代码部分会被包括进来。
  • 静态链接的影响:尽管 Rust 进行了死代码消除,但静态链接仍然可能会导致较大的二进制文件,因为所有必要的代码都被包含在单个文件中。这与动态链接相反,其中二进制文件依赖于外部的共享库。
  • 其他因素:除了标准库和你的代码,还有其他因素可能影响编译输出的大小,例如调试信息、优化等级等。

那么该如何优化编译大小呢?

  • 发布模式:通过使用cargo build --release,你可以告诉Rust进行更多的优化,并去除调试信息。这通常会显著减小生成的二进制文件大小。
  • 去除标准库:对于某些特定应用,如嵌入式系统编程,你可能不需要整个标准库。在这种情况下,你可以考虑使用#![no_std]属性来禁用标准库。
  • 使用strip命令:strip是一个可以移除二进制文件中符号信息的工具,进一步减小文件大小。
  • 其他优化工具和策略:例如,使用upx可以进一步压缩生成的二进制文件。还有其他的Cargo插件和工具,如cargo-bloat,可以帮助你识别和减小二进制文件大小。

优化配置的Cargo.toml

[package]
name = "min-sized-rust"
version = "0.1.0"
authors = ["johnthagen <johnthagen@gmail.com>"]
license-file = "LICENSE.txt"

[dependencies]

[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
strip = true        # Automatically strip symbols from the binary.

UPX (Ultimate Packer for eXecutables)

Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file..

-123456789:表示压缩等级,数字越大压缩等级越高,文件越小,更费时间。500K以下文件默认为8,其他默认为7。
--best:最大压缩率
--lzma:尝试使用LZMA[比NRV更慢,but tighter]
--brute:尝试使用各种压缩方法来提高压缩比[慢]
--ultra-brute:尝试使用更多的压缩参数[非常慢]

-o:输出文件
-k:保留原文件[默认不保留]

最后修改:2023 年 11 月 18 日
如果觉得我的文章对你有用,请随意赞赏