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 = true및 panic = '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.