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


153

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 라이너 프로그램에서 그러한 거대한 이미지를 어떻게 만들 수 있습니까? 가상 머신으로 컴파일 중입니까? 내가 놓친 스트립 명령이 있습니까 (릴리스 빌드 내부의 디버그 정보)? 무슨 일이 일어나고 있는지 이해할 수있는 다른 것이 있습니까?


4
3Mb에는 Hello World뿐만 아니라 플랫폼에 필요한 모든 환경이 포함되어 있다고 생각합니다. Qt에서도 마찬가지입니다. 6 줄 프로그램을 작성하더라도 크기가 6MB가되는 것은 아닙니다. 3Mb에 머무르고 그 후 매우 느리게 성장합니다.
Andrei Nikolaenko

8
@AndreiNikolaenko 알고 있습니다. 그러나 이것은 C처럼 라이브러리를 처리하지 않고 이미지에 필요한 것만 추가하거나 다른 일이 일어나고 있음을 암시합니다.
BitTickler

@ user2225104 내 대답에 따르면, RUST는 C와 동일한 (또는 유사한) 방식으로 라이브러리를 처리하지만 기본적으로 C는 정적 라이브러리를 프로그램으로 컴파일하지 않습니다 (적어도 C ++에서는).
AStopher


1
이것은 구식입니까? rustc 버전 1.35.0 및 cli 옵션이 없으면 크기가 137kb 인 exe를 얻습니다. 이제 자동으로 동적으로 링크되어 컴파일되거나 다른 일이 발생합니까?
itmuckel 2016 년

답변:


139

Rust는 정적 링크를 사용하여 프로그램을 컴파일합니다. 즉, 가장 간단한 Hello world!프로그램 이라도 필요한 모든 라이브러리 가 실행 파일로 컴파일됩니다. 여기에는 Rust 런타임도 포함됩니다.

Rust가 프로그램을 동적으로 링크하도록하려면 명령 행 인수를 사용하십시오 -C prefer-dynamic. 파일 크기가 훨씬 작아 지지만 런타임에 Rust 라이브러리 (런타임 포함)를 프로그램에서 사용할 수 있어야합니다. 즉, 컴퓨터에없는 경우 원래 정적으로 링크 된 프로그램이 차지하는 것보다 더 많은 공간을 차지해야합니다.

이식성을 위해 프로그램을 다른 사람에게 배포하려는 경우와 마찬가지로 Rust 라이브러리와 런타임을 정적으로 링크하는 것이 좋습니다.


4
@ user2225104화물에 대해 잘 모르지만 GitHub의이 버그 보고서에 따르면 불행히도 아직 불가능하지는 않습니다.
AStopher

2
그러나 시스템에 2 개 이상의 rust 실행 파일이있는 경우 동적 링크는 공간 절약을 시작합니다.
binki

15
정적 링크가 거대한 HELLO-WORLD를 설명한다고 생각하지 않습니다. 실제로 사용되는 라이브러리 부분에서만 링크하면 안되고 HELLO-WORLD는 거의 아무것도 사용하지 않습니까?
MaxB

8
BitTicklercargo rustc [--debug or --release] -- -C prefer-dynamic
Zach Mertes

3
@daboross 대단히 감사합니다. 이 관련 RFC를 추적 하고 있습니다 . Rust도 시스템 프로그래밍을 목표로하기 때문에 정말 유감입니다.
Franklin Yu

62

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

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


27
gcc는 정확하게 printf-> 치환을 넣을 수있을만큼 똑똑합니다. 그래서 결과가 동일합니다.
bluss

6
2018 년 현재 공정한 비교를 원한다면 실행 파일을 "스트립"해야한다는 것을 기억하십시오. 안녕하세요. 시스템의 Hello Rust 실행 파일은 무려 5.3MB이지만 모든 디버그 기호를 제거하고 10 % 미만으로 떨어 뜨립니다. 이러한.
Matti Virkkunen

@MattiVirkkunen : 2020 년의 경우; 자연 크기는 더 작아 보이지만 (5.3M 근처), 코드 대 기호의 비율은 여전히 ​​매우 극단적입니다. CentOS 7의 Rust 1.34.0에서 순수 기본 옵션 인 디버그 빌드 strip -s는 1.6M에서 190K로 떨어졌습니다. 릴리스 빌드 (기본값 plus opt-level='s', lto = truepanic = 'abort'크기를 최소화하기 위해)는 623K에서 158K로 떨어집니다.
ShadowRanger

정적 및 동적 사과를 구별하는 방법? 후자는 건강하게 들리지 않습니다.
LF

30

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

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

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

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

strip target/release/<binary>

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


8
일부 통계, hello world의 기본 릴리스 버전 (linux x86_64). 3.5 M, 선호 동적 8904 B, 6392 B 제거
Zitrax

30

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을 참조하십시오.


-10

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

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

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


9
라이브러리 버전을 지정하려면 [...] 특정 라이브러리를 정적으로 링크해야합니다. 아니요, 그렇지 않습니다. 정확한 버전의 라이브러리가 동적으로 연결된 곳에는 많은 코드가 있습니다.
Shepmaster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.