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 - 2023UPX 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 101tUPX!m@/H(O0OStdPo/lib64nux-x86-.so._ITM_deregistCloneTable__gm_.art

原理

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

加壳与解压

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

长处

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

毛病

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

总结

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

所以采纳优化形式如下

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

配置Cargo.toml为如下

[profile.release]strip = truelto = true

参考浏览

Minimizing Rust Binary Size

应用 UPX 压缩可执行文件

How They Work, Featuring UPX