ทำไมการจัดสรรเริ่มต้น C ++ จึงมีขนาดใหญ่กว่า C มาก?


138

เมื่อใช้รหัสเดียวกันการเปลี่ยนคอมไพเลอร์ (จากคอมไพเลอร์ C เป็นคอมไพเลอร์ C ++) จะเปลี่ยนจำนวนหน่วยความจำที่จัดสรร ฉันไม่แน่ใจว่าทำไมถึงเป็นเช่นนี้และต้องการที่จะเข้าใจมากกว่านี้ จนถึงการตอบสนองที่ดีที่สุดที่ฉันได้รับคือ "อาจเป็นสตรีม I / O" ซึ่งไม่ได้อธิบายมากและทำให้ฉันสงสัยเกี่ยวกับ "คุณไม่จ่ายเงินสำหรับสิ่งที่คุณไม่ได้ใช้" แง่มุมของ C ++

ฉันใช้คอมไพเลอร์ Clang และ GCC เวอร์ชัน 7.0.1-8 และ 8.3.0-6 ตามลำดับ ระบบของฉันทำงานบน Debian 10 (Buster) เวอร์ชันล่าสุด มาตรฐานจะทำผ่าน Valgrind Massif

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

รหัสที่ใช้ไม่เปลี่ยนแปลง แต่ไม่ว่าฉันจะคอมไพล์เป็น C หรือเป็น C ++ มันจะเปลี่ยนผลลัพธ์ของเกณฑ์มาตรฐาน Valgrind อย่างไรก็ตามค่ายังคงสอดคล้องกันในคอมไพเลอร์ การจัดสรรรันไทม์ (จุดสูงสุด) สำหรับโปรแกรมดำเนินการดังนี้:

  • GCC (C): 1,032 ไบต์ (1 KB)
  • G ++ (C ++): 73,744 ไบต์, (~ 74 KB)
  • เสียงดังกราว (C): 1,032 ไบต์ (1 KB)
  • เสียงดังกังวาน ++ (C ++): 73,744 ไบต์ (~ 74 KB)

สำหรับการรวบรวมฉันใช้คำสั่งต่อไปนี้:

clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp

สำหรับ Valgrind ฉันจะใช้งานvalgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-langคอมไพเลอร์และภาษาแต่ละภาษาจากนั้นms_printแสดงยอดเขา

ฉันกำลังทำอะไรผิดที่นี่?


11
จะเริ่มต้นด้วยวิธีการที่คุณสร้าง? ตัวเลือกใดที่คุณใช้ และคุณวัดได้อย่างไร คุณรัน Valgrind ได้อย่างไร?
โปรแกรมเมอร์บางคนเพื่อน

17
ถ้าฉันจำได้อย่างถูกต้องคอมไพเลอร์ C ++ สมัยใหม่ต้องเป็นตัวแบบยกเว้นที่ไม่มีประสิทธิภาพในการเข้าtryบล็อกด้วยค่าใช้จ่ายของหน่วยความจำขนาดใหญ่อาจมีตารางกระโดดหรืออะไรบางอย่าง อาจลองรวบรวมโดยไม่มีข้อยกเว้นและดูว่ามีผลกระทบอะไรบ้าง แก้ไข: อันที่จริงแล้วลองปิดการใช้งานคุณสมบัติ c ++ ต่าง ๆ ซ้ำ ๆ เพื่อดูว่ามีผลกระทบอะไรบ้างในหน่วยความจำ
François Andrieux

3
เมื่อคอมไพล์ด้วยclang++ -xcแทนที่จะมีclangการจัดสรรแบบเดียวกันซึ่งแนะนำอย่างยิ่งเนื่องจากไลบรารีที่เชื่อมโยง
Justin

14
@bwill จะเป็น C ++ แน่นอนฉันไม่เห็นส่วนใด ๆ ของข้อกำหนด C ++ ที่แบ่ง ... นอกเหนือจากที่อาจรวมถึง stdio.h มากกว่า cstdio แต่ได้รับอนุญาตอย่างน้อยในรุ่น C ++ ที่เก่ากว่า คุณคิดว่าอะไร "ผิดรูปแบบ" ในโปรแกรมนี้?
Vality

4
ฉันคิดว่ามันน่าสงสัยว่าคอมไพเลอร์ gcc และ clang เหล่านั้นสร้างจำนวนไบต์Cที่แน่นอนในโหมดเดียวกันและC++โหมดไบต์จำนวนเดียวกันแน่นอน คุณได้ทำการถอดความผิดพลาดหรือไม่?
RonJohn

คำตอบ:


149

การใช้ฮีปมาจากไลบรารีมาตรฐาน C ++ มันจัดสรรหน่วยความจำสำหรับใช้ภายในห้องสมุดเมื่อเริ่มต้น หากคุณไม่ได้เชื่อมโยงกับมันควรมีความแตกต่างเป็นศูนย์ระหว่างรุ่น C และ C ++ ด้วย GCC และ Clang คุณสามารถรวบรวมไฟล์ด้วย:

g ++ -Wl, - main.cpp ตามที่ต้องการ

สิ่งนี้จะสั่งให้ตัวเชื่อมโยงไม่ทำการเชื่อมโยงกับไลบรารีที่ไม่ได้ใช้ ในโค้ดตัวอย่างของคุณจะไม่ใช้ไลบรารี C ++ ดังนั้นจึงไม่ควรเชื่อมโยงกับไลบรารีมาตรฐาน C ++

คุณสามารถทดสอบสิ่งนี้ด้วยไฟล์ C หากคุณรวบรวมด้วย:

gcc main.c -lstdc ++

การใช้ฮีปจะปรากฏขึ้นอีกครั้งแม้ว่าคุณจะสร้างโปรแกรม C แล้วก็ตาม

การใช้ฮีปขึ้นอยู่กับการใช้ไลบรารี่ C ++ เฉพาะที่คุณใช้ ในกรณีของคุณที่ห้องสมุด GNU C ++ libstdc ++ การใช้งานอื่นอาจไม่จัดสรรจำนวนหน่วยความจำเท่ากันหรืออาจไม่จัดสรรหน่วยความจำใด ๆ เลย (อย่างน้อยก็ไม่ได้อยู่ที่การเริ่มต้น) ไลบรารี่ LLVM C ++ ( libc ++ ) ตัวอย่างเช่นไม่ทำการจัดสรรฮีปเมื่อเริ่มต้น เครื่อง:

clang ++ -stdlib = libc ++ main.cpp

การใช้ฮีปเหมือนกับการเชื่อมโยงไม่ได้เลย

(หากการรวบรวมล้มเหลวอาจไม่ได้ติดตั้ง libc ++ ชื่อแพ็คเกจมักจะมี "libc ++" หรือ "libcxx")


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

4
@Nat เดาของฉันคือเวลา dev จะรวบรวมช้า เมื่อคุณพร้อมที่จะสร้างงานสร้างคุณจะต้องเปิดใช้งาน นอกจากนี้ใน codebase ปกติ / ขนาดใหญ่ความแตกต่างอาจน้อยที่สุด (ถ้าคุณใช้ไลบรารี STD จำนวนมาก ฯลฯ )
DarcyThomas

24
@Nat แฟล็ก-Wl,--as-neededจะลบไลบรารีที่คุณระบุใน-lแฟล็กของคุณแต่คุณไม่ได้ใช้งานจริง ดังนั้นหากคุณไม่ได้ใช้ห้องสมุดคุณก็ไม่ต้องเชื่อมโยงกับห้องสมุด คุณไม่ต้องการแฟลกนี้สำหรับสิ่งนี้ อย่างไรก็ตามหากระบบการสร้างของคุณเพิ่มห้องสมุดมากเกินไปและมันเป็นงานที่ต้องทำความสะอาดทั้งหมดและเชื่อมโยงสิ่งที่จำเป็นเท่านั้นคุณสามารถใช้การตั้งค่าสถานะนี้แทน ไลบรารี่มาตรฐานเป็นข้อยกเว้นเนื่องจากมีการเชื่อมโยงโดยอัตโนมัติ นี่คือกรณีมุม
Nikos C.

36
@Nat - ตามต้องการอาจมีผลข้างเคียงที่ไม่พึงประสงค์มันทำงานได้โดยการตรวจสอบว่าคุณใช้สัญลักษณ์ใด ๆ ของห้องสมุดและเตะสิ่งที่ล้มเหลวออกจากการทดสอบ แต่: ไลบรารียังสามารถทำสิ่งต่าง ๆ โดยนัยตัวอย่างเช่นถ้าคุณมีอินสแตนซ์ C ++ แบบคงที่ในไลบรารีตัวสร้างของมันจะถูกเรียกโดยอัตโนมัติ มีบางกรณีที่จำเป็นต้องมีห้องสมุดที่คุณไม่จำเป็นต้องโทรเข้ามา แต่ก็มีอยู่จริง
Norbert Lange

3
@NikosC Buildsystems ไม่ทราบโดยอัตโนมัติว่าแอปพลิเคชันของคุณใช้สัญลักษณ์ใดและไลบรารี่ใดที่ใช้มัน การทำให้ถูกต้องนั้นค่อนข้างลำบากอย่างน้อยที่สุดสำหรับไลบรารีรันไทม์ฐาน แต่สำหรับกรณีที่ไม่ค่อยเกิดขึ้นคุณจำเป็นต้องมีห้องสมุดคุณควรใช้ - ไม่จำเป็นสำหรับห้องสมุดนั้นและออก - จำเป็นทุกที่อื่น usecase ที่ฉันเห็นคือไลบรารีสำหรับการติดตาม / การดีบัก (lttng) และไลบรารีที่ทำบางสิ่งบางอย่างในการพิสูจน์ตัวตน / การเชื่อมต่อ
Norbert Lange

16

ไม่ว่าจะเป็น GCC หรือ Clang นั้นก็เป็นคอมไพเลอร์ แต่จริงๆแล้วมันเป็นโปรแกรมไดรเวอร์ Toolchain นั่นหมายถึงพวกเขาเรียกใช้คอมไพเลอร์แอสเซมเบลอร์และลิงเกอร์

หากคุณรวบรวมรหัสของคุณด้วยคอมไพเลอร์ C หรือ C ++ คุณจะได้รับการประกอบที่เหมือนกัน ผู้ประกอบจะผลิตวัตถุเดียวกัน ความแตกต่างก็คือไดรเวอร์ toolchain จะให้อินพุตที่แตกต่างกันไปยัง linker สำหรับสองภาษาที่แตกต่างกัน: startups ที่แตกต่างกัน (C ++ ต้องการรหัสสำหรับการเรียกใช้งาน constructors และ destructors สำหรับวัตถุที่มีระยะเวลาคงที่ เฟรมเพื่อรองรับคลี่คลายในระหว่างการประมวลผลข้อยกเว้นตัวอย่าง) ไลบรารีมาตรฐาน C ++ (ซึ่งมีวัตถุของระยะเวลาการจัดเก็บแบบคงที่ที่ระดับเนมสเปซ) และอาจเป็นไลบรารีรันไทม์เพิ่มเติม (ตัวอย่างเช่น libgcc พร้อมโครงสร้างพื้นฐาน

สรุปแล้วมันไม่ใช่คอมไพเลอร์ที่ทำให้เกิดรอยเท้าเพิ่มขึ้น แต่เป็นการเชื่อมโยงของสิ่งที่คุณเลือกใช้โดยการเลือกภาษา C ++

เป็นความจริงที่ว่า C ++ มีปรัชญา "จ่ายเฉพาะสิ่งที่คุณใช้" เท่านั้น แต่ด้วยการใช้ภาษาคุณจะต้องจ่ายให้ คุณสามารถปิดการใช้งานบางส่วนของภาษา (RTTI, การจัดการข้อยกเว้น) แต่แล้วคุณไม่ได้ใช้ C ++ อีกต่อไป ดังที่ได้กล่าวไว้ในคำตอบอื่นถ้าคุณไม่ได้ใช้ไลบรารี่มาตรฐานคุณสามารถสั่งให้คนขับรถออกไป (-, - ตามต้องการ) แต่ถ้าคุณไม่ได้ใช้ฟีเจอร์ใด ๆ ของ C ++ หรือไลบรารีทำไมคุณถึงเลือก C ++ เป็นภาษาโปรแกรม


ความจริงที่ว่าการเปิดใช้งานการจัดการข้อยกเว้นมีค่าใช้จ่ายแม้ว่าคุณจะไม่ได้ใช้จริงก็เป็นปัญหา ไม่ปกติสำหรับคุณสมบัติ C ++ โดยทั่วไปและเป็นสิ่งที่กลุ่มทำงานมาตรฐาน C ++ พยายามคิดหาวิธีแก้ไข ดูคำปราศรัยสำคัญของ Herb Sutter ที่ ACCU 2019 การแยกส่วน C ++: ทำให้ข้อยกเว้นราคาไม่แพงและใช้งานได้มากขึ้น มันเป็นความจริงที่โชคร้ายในปัจจุบัน C ++ และข้อยกเว้น C ++ แบบดั้งเดิมอาจมีค่าใช้จ่ายนั้นเสมอแม้ว่า / เมื่อมีการเพิ่มกลไกใหม่สำหรับข้อยกเว้นใหม่ด้วยคำหลัก
ปีเตอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.