Minimizing Rust Binary Size

栏目: IT技术 · 发布时间: 5年前

内容简介:This repository demonstrates how to minimize the size of a Rust binary.By default, Rust optimizes for execution speed rather than binary size, since for the vast majority of applications this is ideal. But for situations where a developer wants to optimize

Minimizing Rust Binary Size

Build Status
Travis

This repository demonstrates how to minimize the size of a Rust binary.

By default, Rust optimizes for execution speed rather than binary size, since for the vast majority of applications this is ideal. But for situations where a developer wants to optimize for binary size instead, Rust provides mechanisms to accomplish this.

Build in Release Mode

By default, cargo build builds the Rust binary in debug mode. Debug mode disables many optimizations, which helps debuggers (and IDEs that run them) provide a better debugging experience. Debug binaries can be 30% or more larger than release binaries.

To minimize binary size, build in release mode:

$ cargo build --release

strip Symbols from Binary

Note: Looking for a tool to help automated strip ing? Check out cargo-strip or follow Cargo #3483 .

By default on Linux and macOS, symbol information is included in the compiled .elf file. This information is not needed to properly execute the binary. To remove this, run strip on the .elf file:

$ strip target/release/min-sized-rust

Optimize For Size

Cargo defaults its optimization level to 3 for release builds , which optimizes the binary for speed. To instruct Cargo to optimize for minimal binary size, use the z optimization level in Cargo.toml :

[profile.release]
opt-level = 'z'  # Optimize for size.

Enable Link Time Optimization (LTO)

By default, Cargo instructs compilation units to be compiled and optimized in isolation . LTO instructs the linker to optimize at the link stage. This can, for example, remove dead code and often times reduces binary size.

Enable LTO in Cargo.toml :

[profile.release]
lto = true

Remove Jemalloc

As of Rust 1.32, jemalloc is removed by default . If using Rust 1.32 or newer, no action is needed to reduce binary size regarding this feature.

Prior to Rust 1.32, to improve performance on some platforms Rust bundled jemalloc , an allocator that often outperforms the default system allocator. Bundling jemalloc added around 200KB to the resulting binary, however.

To remove jemalloc on Rust 1.28 - Rust 1.31, add this code to the top of main.rs :

use std::alloc::System;

#[global_allocator]
static A: System = System;

Reduce Parallel Code Generation Units to Increase Optimization

By default , Cargo specifies 16 parallel codegen units for release builds. This improves compile times, but prevents some optimizations.

Set this to 1 in Cargo.toml to allow for maximum size reduction optimizations:

[profile.release]
codegen-units = 1

Abort on Panic

Note: Up to this point, the features discussed to reduce binary size did not have an impact on the behaviour of the program (only its execution speed). This feature does have an impact on behavior.

By default , when Rust code encounters a situation when it must call panic!() , it unwinds the stack and produces a helpful backtrace. The unwinding code, however, does require extra binary size. rustc can be instructed to abort immediately rather than unwind, which removes the need for this extra unwinding code.

Enable this in Cargo.toml :

[profile.release]
panic = 'abort'

Optimize libstd with Xargo

Note: Xargo is currently in maintenance status , but eventually the features used below should make their way into Cargo.

Example project is located in the xargo folder.

Rust ships pre-built copies of the standard library ( libstd ) with its toolchains. This means that developers don't need to build libstd every time they build their applications. libstd is statically linked into the binary instead.

While this is very convenient there are several drawbacks if a developer is trying to aggressively optimize for size.

libstd
libstd

This is where Xargo comes in. Xargo is able to compile libstd with your application from the source. It does this with the rust-src component that rustup conveniently provides.

Add a Xargo.toml file to the root of your project (this doesn't replace Cargo.toml , just is in addition):

[dependencies]
std = {default-features=false}

Install the appropriate toolchain and Xargo:

$ rustup toolchain install nightly
$ rustup override set nightly
$ rustup component add rust-src
$ cargo install xargo

Build using Xargo:

# Find your host's target triple. 
$ rustc -vV
...
host: x86_64-apple-darwin

# Use that target triple when building with Xargo.
$ xargo build --target x86_64-apple-darwin --release

Remember to strip the resulting executable. On macOS, the final binary size is reduced to 51KB.

Remove panic String Formatting with panic_immediate_abort

Example project is located in the panic_immediate_abort folder.

Even if panic = abort is specified in Cargo.toml , rustc will still include panic strings and formatting code in final binary by default. An unstable panic_immediate_abort feature has been merged into the nightly rustc compiler to address this.

To use this, repeat the instructions above to use Xargo, but instead use the following Xargo.toml :

[dependencies]
std = {default-features=false, features=["panic_immediate_abort"]}

Remember to strip the resulting executable. On macOS, the final binary size is reduced to 30KB.

Remove core::fmt with #![no_main] and Careful Usage of libstd

Example project is located in the no_main folder.

This section was contributed in part by @vi

Up until this point, we haven't restricted what utilities we used from libstd . In this section we will restrict our usage of libstd in order to reduce binary size further.

If you want an executable smaller than 20 kilobytes, Rust's string formatting code, core::fmt must be removed. panic_immediate_abort only removes some usages of this code. There is a lot of other code that uses formatting in some of cases. That includes Rust's "pre-main" code in libstd .

By using a C entry point (by added the #![no_main] attribute) , managing stdio manually, and carefully analyzing which chunks of code you or your dependencies include, you can sometimes make use of libstd while avoiding bloated core::fmt .

Expect the code to be hacky and unportable, with more unsafe{} s than usual. It feels like no_std , but with libstd .

Start with an empty executable, ensure xargo bloat --release --target=... contains no core::fmt or something about padding. Add (uncomment) a little bit. See that xargo bloat now reports drastically more. Review source code that you've just added. Probably some external crate or a new libstd function is used. Recurse into that with your review process (it requires [replace] Cargo dependencies and maybe digging in libstd ), find out why it weighs more than it should. Choose alternative way or patch dependencies to avoid unnecessary features. Uncomment a bit more of your code, debug exploded size with xargo bloat and so on.

On macOS, the final stripped binary is reduced to 8KB.

Removing libstd with #![no_std]

Example project is located in the no_std folder.

Up until this point, our application was using the Rust standard library, libstd . libstd provides many convenient, well tested cross platform APIs and data types. But if a user wants to reduce binary size to an equivalent C program size, it is possible to depend only on libc .

It's important to understand that there are many drawbacks to this approach. For one, you'll likely need to write a lot of unsafe code and lose access to a majority of Rust crates that depend on libstd . Nevertheless, it is one (albeit extreme) option to reducing binary size.

A strip ed binary built this way is around 8KB.

#![no_std]
#![no_main]

extern crate libc;

#[no_mangle]
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize {
    // Since we are passing a C string the final null character is mandatory.
    const HELLO: &'static str = "Hello, world!\n\0";
    unsafe {
        libc::printf(HELLO.as_ptr() as *const _);
    }
    0
}

#[panic_handler]
fn my_panic(_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

Tools

  • cargo-bloat - Find out what takes most of the space in your executable.

References


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算机程序的构造和解释

计算机程序的构造和解释

Harold Abelson、Gerald Jay Sussman、Julie Sussman / 裘宗燕 / 机械工业出版社 / 2004-2 / 45.00元

《计算机程序的构造和解释(原书第2版)》1984年出版,成型于美国麻省理工学院(MIT)多年使用的一本教材,1996年修订为第2版。在过去的二十多年里,《计算机程序的构造和解释(原书第2版)》对于计算机科学的教育计划产生了深刻的影响。第2版中大部分重要程序设计系统都重新修改并做过测试,包括各种解释器和编译器。作者根据其后十余年的教学实践,还对其他许多细节做了相应的修改。 海报:一起来看看 《计算机程序的构造和解释》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具