关于rust:Rust二进制文件大小优化

42次阅读

共计 3211 个字符,预计需要花费 9 分钟才能阅读完成。

Rust二进制文件大小优化,在优化二进制文件过程中,编译工夫会减少,然而个别编译时候的优化会放慢程序执行速度

初始化我的项目

Ubuntu22 下面进行试验

创立我的项目

$ cargo new demo

批改 Cargo.toml 如下,须要退出一点罕用的库

[dependencies]
tokio = {version = "1", features = ["full"]}
tracing = "0.1"
tracing-subscriber = "0.3"

批改 main.rs 如下

#[tokio::main]
async fn main() {tracing_subscriber::fmt::init();
    loop {tokio::time::sleep(std::time::Duration::from_secs(1)).await;
        tracing::info!("Hello, world!");
    }
}

debugrelease 的大小比照

编译

$ cargo build
$ cargo build -r

查看大小

$ ll target/*           
target/debug:
总计 28M
-rwxrwxr-x  2 gong gong  28M 四月   25 15:47 demo
...

target/release:
总计 5.0M
-rwxrwxr-x  2 gong gong 5.0M 四月   25 15:46 demo
....

可见 release 版本二进制大概是 debug 版本的 17%,后续所有二进制优化采纳release 5M 大小为基准,每次优化都只改变一个配置项作为比照

优化符号信息symbols

LinuxmacOS上,默认状况下,符号信息蕴含在编译的 .elf 文件中,正确执行二进制文件不须要此信息

配置Cross.toml

[profile.release]
strip = true 

编译之后 demo 二进制变为887k,约为之前的17%,成果好

这个配置项在我的项目比拟大的时候,比方最终二进制是 100M 优化成果就不怎么显著了,个别符号信息是会减小几 M 大小,存在一个下限

配置优化level

默认的 release opt-level 是 3,debug opt-level是 0

[profile.release]
opt-level = "z" 

优化后 demo 变为 5.1M 了,二进制反而变大,很多文档都提到要配置这个优化项,然而素来没发现有什么成果

详情参考官网文档

https://doc.rust-lang.org/cargo/reference/profiles.html

启用链接时优化

默认状况下,Cargo批示编译单元独自编译和优化,LTO批示链接器在链接阶段进行优化,例如,这能够删除无用代码,有些代码段不会被调用到,并缩小二进制大小

默认 release 是没有开启这个配置,开启这个配置后如果我的项目比拟大,则会大幅度减少编译工夫,我的项目依赖越多,代码总量越多,优化成果越显著

[profile.release]
lto = true

编译后二进制变为2.6M,约为之前的52%,成果好

缩小并行代码生成单元

Cargo为公布版本指定了 16 个并行代码单元,这改善了编译工夫,但障碍了一些优化,如果配置为 1,就能够最大水平防止并行代码单元中的障碍优化

[profile.release]
codegen-units = 1

编译后文件大小为4.8M,约为之前的96%,成果不怎么显著

配置程序 panic 时候的行为

Rust 代码遇到必须调用 panic!() 的状况时,它会开展堆栈并生成有用的回溯,开展代码须要额定的二进制,能够调整 Rust 立刻停止程序而不是开展栈信息,这样就不须要额定的开展代码

[profile.release]
panic = "abort"

编译后程序大小为 4.7M,约为之前的97.5%,这样配置的话程序如果报错就看不到什么中央panic 了,会比拟影响线上环境体验,不举荐应用

大杀器UPX

upx是一个二进制压缩工具,反对各类二进制可执行文件的压缩,rust/golang/c.....,在大部分状况下能够把二进制执行文件压缩到原来的 30% 左右,压缩后的文件能够间接由零碎执行,反对多零碎和平台

我的项目地址

装置的时候尽量去官网我的项目的 release 获取到最新的 release,外面蕴含一个upx 可执行文件用于压缩二进制程序

https://github.com/upx/upx

upx反对不同的压缩级别1-9,最高压缩级别是9

个别执行 upx -9 文件 即可

$ upx -9 target/release/demo
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2023
UPX 4.0.2       Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 30th 2023

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   5178056 ->   1173024   22.65%   linux/amd64   demo                          

Packed 1 file.

之后查看二进制可执行文件变为了1.2M,约为原来的22.6%

linux下面应用 strings 查看二进制文件

查看压缩前的可执行文件内容

$ strings target/release/demo |head -n 10
/lib64/ld-linux-x86-64.so.2
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
_Unwind_Resume
_Unwind_Backtrace
_Unwind_GetLanguageSpecificData
_Unwind_GetIPInfo
_Unwind_GetDataRelBase
_Unwind_GetRegionStart

查看压缩后的内容,能够看到 UPX 把结尾的信息改写了

$ strings target/release/demo |head -n 10
1tUPX!
m@/H
(O0OS
tdPo
/lib64
nux-x86-
.so.
_ITM_deregist
CloneTable
__gm_.art

原理

采纳二进制程序加壳技术,UPX 将程序压缩,并在头部退出解压的程序,加壳过的程序能够间接运行,然而不能查看源代码,要通过脱壳才能够查看源代码

加壳与解压

  • 在文件头里加了一段指令,通知 CPU,怎么能力解压本人(在解压时候执行)
  • 利用非凡的算法,对 EXEDLL,其余二进制文件里的资源进行压缩,相似zip 的成果
  • 压缩之后的文件,能够独立运行,给可执行的文件加上个外衣
  • 加壳工具解压过程齐全荫蔽,都在内存中实现,用户执行的只是这个外壳程序
  • 执行时候,壳就会把原来的程序在内存中解开,解开后,当前的就交给真正的程序

长处

  • UPX 能够压缩各种类型的可执行文件,压缩效率十分可观
  • 压缩后的文件能够间接由操作系统执行
  • 压缩过程不会批改源文件,也就意味着解压后间接能够失去原始文件
  • 不会产生额定的动静库调用
  • 没有运行时性能损失
  • 反跟踪、被人跟踪调试、避免程序被他人动态剖析,爱护你程序数据的完整性,避免被程序修改和被窥视底细

毛病

  • 运行的程序不会共享数据段(汇编),所以多实例运行的程序不适宜压缩
  • 应用 lddsize 命令无奈获取到程序的无效信息
  • 关上时耗费更多的 CPU 资源(执行的时候会应用解压算法对压缩后的可执行文件进行解压运算)
  • 在运行时占用更多的内存(次要是多了几兆 UPX 解压程序)

总结

很多优化形式个别都是压缩成果不怎么显著,然而会减少编译耗时或者影响程序应用体验

所以采纳优化形式如下

  • 启用链接时优化
  • 除去不必要的Symbols
  • 采纳 upx 进一步压缩

配置 Cargo.toml 为如下

[profile.release]
strip = true
lto = true

参考浏览

Minimizing Rust Binary Size

应用 UPX 压缩可执行文件

How They Work, Featuring UPX

正文完
 0