ทำไมไฟล์สนิมถึงใหญ่มาก?


153

เพิ่งพบสนิมและได้อ่านสองบทแรกของเอกสารฉันพบวิธีและวิธีที่พวกเขากำหนดภาษาที่น่าสนใจเป็นพิเศษ ดังนั้นฉันตัดสินใจที่จะทำให้นิ้วของฉันเปียกและเริ่มด้วย Hello world ...

ฉันทำเช่นนั้นบน Windows 7 x64 btw

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

การออกcargo buildและดูผลลัพธ์ในtargets\debugฉันพบผลลัพธ์ที่.exeเป็น 3MB หลังจากการค้นหาบางอย่าง (เอกสารของการตั้งค่าสถานะบรรทัดคำสั่งขนส่งสินค้าหายาก ... ) ฉันพบ--releaseตัวเลือกและสร้างการสร้างการเปิดตัว ด้วยความประหลาดใจของฉันขนาด. exe มีขนาดเล็กลงเพียงเล็กน้อยเท่านั้น: 2.99MB แทนที่จะเป็น 3MB

ดังนั้นการสารภาพว่าฉันเป็นมือใหม่สำหรับสนิมและระบบนิเวศน์ของมันความคาดหวังของฉันจะเป็นไปได้ว่าภาษาการเขียนโปรแกรมระบบจะสร้างสิ่งที่กะทัดรัด

ใครสามารถอธิบายอย่างละเอียดเกี่ยวกับสิ่งที่ Rust รวบรวมได้เป็นไปได้อย่างไรที่จะสามารถสร้างภาพขนาดใหญ่เช่นนี้จากโปรแกรม 3 สายการบิน? มันรวบรวมไปยังเครื่องเสมือนหรือไม่? มีคำสั่งสตริปที่ฉันพลาดไปหรือไม่ มีอะไรอีกบ้างที่ทำให้เข้าใจได้ว่าเกิดอะไรขึ้น


4
ฉันคิดว่า 3Mb มี Hello World ไม่เพียง แต่ยังมีสภาพแวดล้อมที่จำเป็นทั้งหมดสำหรับแพลตฟอร์ม สิ่งเดียวกันนี้สามารถเห็นได้ด้วย Qt ไม่ได้หมายความว่าถ้าคุณเขียนโปรแกรม 6 บรรทัดขนาดจะกลายเป็น 6 Mb มันจะอยู่ที่ 3Mb และจะเติบโตช้ามากหลังจากนั้น
Andrei Nikolaenko

8
@AndreiNikolaenko ฉันรู้ว่า แต่นี่เป็นการบอกใบ้ว่าพวกเขาไม่ได้จัดการกับไลบรารีอย่างที่ C ทำขึ้นโดยเพิ่มเฉพาะสิ่งที่จำเป็นต้องใช้กับรูปภาพหรือมีสิ่งอื่นเกิดขึ้น
BitTickler

@ user2225104 ดูคำตอบของฉัน RUST จัดการกับไลบรารีด้วยวิธีเดียวกัน (หรือคล้ายกัน) เหมือนกับ C แต่โดยปกติ C จะไม่รวบรวมไลบรารีสแตติกในโปรแกรมของคุณ (อย่างน้อยใน C ++)
AStopher


1
สิ่งนี้ล้าสมัยหรือไม่ ด้วยรุ่นสนิม 1.35.0 และไม่มีตัวเลือก cli ฉันได้รับ exe ที่มีขนาด 137kb มันรวบรวมโดยอัตโนมัติเชื่อมโยงแบบไดนามิกในขณะนี้หรือมีสิ่งอื่นเกิดขึ้นในระหว่างนี้?
itmuckel

คำตอบ:


139

Rust ใช้การลิงก์แบบสแตติกเพื่อรวบรวมโปรแกรมซึ่งหมายความว่าไลบรารีทั้งหมดที่ต้องการโดยแม้แต่Hello world!โปรแกรมที่ง่ายที่สุดจะถูกรวบรวมไว้ในความสามารถในการปฏิบัติการของคุณ ซึ่งรวมถึงรันไทม์ของ Rust

เพื่อบังคับให้สนิมกับโปรแกรมการเชื่อมโยงแบบไดนามิกใช้อาร์กิวเมนต์บรรทัดคำสั่ง-C prefer-dynamic; สิ่งนี้จะส่งผลให้ไฟล์มีขนาดเล็กลงมากแต่จะต้องใช้ไลบรารี Rust (รวมถึงรันไทม์) เพื่อให้โปรแกรมของคุณพร้อมใช้งานขณะใช้งาน นี่หมายความว่าคุณจำเป็นต้องจัดเตรียมสิ่งเหล่านี้หากคอมพิวเตอร์ไม่ได้ใช้พื้นที่เพิ่มขึ้นกว่าที่โปรแกรมเชื่อมโยงแบบคงที่ดั้งเดิมของคุณจะใช้เวลา

เพื่อความสะดวกในการพกพาฉันขอแนะนำให้คุณเชื่อมโยงไลบรารี Rust และรันไทม์ในแบบที่คุณเคยทำหากคุณต้องการแจกจ่ายโปรแกรมของคุณให้ผู้อื่น


4
@ user2225104 ไม่แน่ใจเกี่ยวกับ Cargo แต่จากรายงานข้อผิดพลาดใน GitHubนี้ยังไม่สามารถทำได้
AStopher

2
แต่ทันทีที่คุณมีสนิมมากกว่า 2 ตัวบนระบบการเชื่อมโยงแบบไดนามิกจะเริ่มประหยัดพื้นที่ของคุณ…
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 เม.ย. 5 14:26 printf.c
-rwxr-xr-x 1 aij aij 6696 5 เม.ย. 14:27 printf.dyn
-rwxr-xr-x 1 aij aij 829344 5 เม.ย. 14:27 printf.static
-rw-r - r-- 1 aij aij 59 เม.ย. 5 14:26 น
-rwxr-xr-x 1 aij aij 6696 5 เม.ย. 14:27 puts.dyn
-rwxr-xr-x 1 aij aij 829344 5 เม.ย. 14:27
-rwxr-xr-x 1 aij aij 8712 5 เม.ย. 14:28 สนิม.dyn
-rw-r - r-- 1 aij aij 46 เม.ย. 5 14:09 น. สนิม
-rwxr-xr-x 1 aij aij 661496 5 เม.ย. 14:28 สนิม

สิ่งเหล่านี้ถูกคอมไพล์ด้วย gcc (Debian 4.9.2-10) 4.9.2 และ rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (สร้าง 2015-04-03), ทั้งที่มีตัวเลือกเริ่มต้นและ-staticสำหรับ gcc และ-C prefer-dynamicสำหรับ rustc

ฉันมี C hello world สองเวอร์ชันเพราะฉันคิดว่าการใช้puts()อาจเชื่อมโยงกับหน่วยการคอมไพล์น้อยลง

หากคุณต้องการลองทำซ้ำบน 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
ตั้งแต่ปีพ. ศ. 2561 ถ้าคุณต้องการเปรียบเทียบที่เป็นธรรมอย่าลืม "เปลื้อง" ไฟล์ที่เรียกใช้งานเช่นเดียวกับสวัสดีชาวโลกสนิมไฟล์ที่รันได้บนระบบของฉันคือ 5.3MB ขนาดมหึมา แต่ลดลงเหลือน้อยกว่า 10% เมื่อคุณลบสัญลักษณ์แก้ปัญหาทั้งหมด อย่างเช่น
Matti Virkkunen

@MattiVirkkunen: ยังคงเป็นกรณีในปี 2020 ขนาดธรรมชาติดูเล็กลง (ไม่มีที่ไหนใกล้ 5.3M) แต่อัตราส่วนของสัญลักษณ์ต่อรหัสยังคงค่อนข้างรุนแรง debug build, อ็อพชันเริ่มต้นอย่างหมดจดบน Rust 1.34.0 บน CentOS 7 ซึ่งถอดstrip -sมาจาก 1.6M ถึง 190K ปล่อยสร้าง (ค่าเริ่มต้นบวกopt-level='s', lto = trueและpanic = 'abort'เพื่อลดขนาด) ลดลงจาก 623K เพื่อ 158k
ShadowRanger

วิธีแยกแยะแอปเปิ้ลแบบคงที่และแบบไดนามิก หลังไม่ฟังดูแข็งแรง
LF

30

เมื่อรวบรวมกับ Cargo คุณสามารถใช้การเชื่อมโยงแบบไดนามิก:

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

สำหรับภาพรวมของวิธีทั้งหมดในการลดขนาดของไบนารีสนิมดูที่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] libstdดูmin-sized-rustรายละเอียดที่


-10

นี่คือคุณสมบัติไม่ใช่ข้อผิดพลาด!

คุณสามารถระบุเวอร์ชันของไลบรารี (ในไฟล์ Cargo.toml ที่เชื่อมโยงของโครงการ ) ที่ใช้ในโปรแกรม (แม้แต่เวอร์ชันโดยนัย) เพื่อรับรองความเข้ากันได้ของเวอร์ชันไลบรารี ในทางกลับกันนี้ต้องการให้ไลบรารีที่เฉพาะเจาะจงนั้นเชื่อมโยงกับโปรแกรมที่สร้างขึ้นแบบคงที่เพื่อสร้างอิมเมจเวลาทำงานขนาดใหญ่

เฮ้มันไม่ใช่ 1978 อีกต่อไป - หลายคนมี RAM มากกว่า 2 MB ในคอมพิวเตอร์ :-)


9
ระบุรุ่นห้องสมุด [... ] ต้องการให้มีการเชื่อมโยงกับห้องสมุดเฉพาะ - ไม่มันไม่ มีโค้ดจำนวนมากที่ไลบรารีรุ่นที่แน่นอนเชื่อมโยงกันแบบไดนามิก
Shepmaster
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.