[rust] Rust 실행 파일이 왜 그렇게 큰가요?

Rust를 발견하고 문서의 처음 두 장을 읽은 후에는 언어를 특히 흥미롭게 정의한 방식과 접근 방식을 찾았습니다. 그래서 손가락을 적시고 Hello world로 시작하기로 결정했습니다 …

Windows 7 x64, btw에서 그렇게했습니다.

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

발행 cargo build하고 결과를 targets\debug보면 결과 .exe는 3MB입니다. 일부 검색 (화물 명령 줄 플래그 문서를 찾기가 어렵습니다 …) 후 --release옵션을 발견 하고 릴리스 빌드를 작성했습니다. 놀랍게도 .exe 크기는 3MB가 아닌 2.99MB로 미미합니다.

따라서 Rust와 그 생태계의 초보자라고 고백하면서, 시스템 프로그래밍 언어가 컴팩트 한 것을 생산할 것으로 기대했을 것입니다.

누구나 Rust가 컴파일하는 것에 대해 자세히 설명 할 수 있습니까? 3 라이너 프로그램에서 그러한 거대한 이미지를 어떻게 만들 수 있습니까? 가상 머신으로 컴파일 중입니까? 내가 놓친 스트립 명령이 있습니까 (릴리스 빌드 내부의 디버그 정보)? 무슨 일이 일어나고 있는지 이해할 수있는 다른 것이 있습니까?



답변

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 4 월 5 일 14:26 printf.c
-rwxr-xr-x 1 aij aij 6696 4 월 5 일 14:27 printf.dyn
-rwxr-xr-x 1 aij aij 829344 4 월 5 일 14:27 printf. 정적
-rw-r--r-- 1 aij aij 59 4 5 14:26 puts.c
-rwxr-xr-x 1 aij aij 6696 4 월 5 일 14:27 puts.dyn
-rwxr-xr-x 1 aij aij 829344 4 월 5 일 14:27 puts.static
-rwxr-xr-x 1 aij aij 8712 4 월 5 일 14:28 rust.dyn
-rw-r--r-- 1 aij aij 46 4 월 5 일 14:09 rust.rs
-rwxr-xr-x 1 aij aij 661496 4 월 5 일 14:28 녹. 정적

이들은 GCC (데비안 4.9.2-10) 4.9.2 컴파일 및 1.0.0-야간 (d17d6e7f1 2015년 4월 2일) (2015년 4월 3일을 내장), 기본 옵션을 모두와와 rustc 된 -staticGCC과 -C prefer-dynamic에 대한 rustc.

사용하는 puts()컴파일 단위 수가 더 적을 것이라고 생각했기 때문에 C hello world의 두 가지 버전이 있습니다.

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!");
}

또한, 다른 양의 디버깅 정보 또는 다른 최적화 수준도 차이를 만들 수 있습니다. 그러나 나는 당신이 큰 차이를보고 있다면 정적 링크와 동적 링크 때문입니다.


답변

화물로 컴파일 할 때 동적 연결을 사용할 수 있습니다.

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

바이너리가 동적으로 연결되므로 바이너리의 크기가 크게 줄어 듭니다.

Linux에서는 최소한 다음 strip명령을 사용하여 이진 기호를 제거 할 수도 있습니다 .

strip target/release/<binary>

이것은 대부분 바이너리의 크기를 대략 절반으로 줄입니다.


답변

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
  2. strip결과 바이너리에서 실행 합니다.

nightlyRust를 사용하여 더 많은 것을 할 수 있지만 min-sized-rust불안정한 기능을 사용하여 시간이 지남에 따라 변경 되는 정보를 그대로 두겠습니다 .

#![no_std]Rust를 제거 하는 데 사용할 수도 있습니다 libstd. 자세한 내용 min-sized-rust을 참조하십시오.


답변

이것은 버그가 아닌 기능입니다!

라이브러리 버전 호환성을 보장하기 위해 프로그램에서 사용되는 라이브러리 버전 ( 프로젝트의 관련 Cargo.toml 파일 에서)을 암시 적 버전으로 지정할 수 있습니다 . 반면에, 특정 라이브러리는 실행 파일에 정적으로 연결되어 큰 런타임 이미지를 생성해야합니다.

이봐, 그것은 더 이상 1978 년이 아닙니다-많은 사람들이 컴퓨터에 2MB 이상의 RAM을 가지고 있습니다 🙂


답변