ทำไมการคอมไพล์ C ++ ใช้เวลานานมาก


540

การรวบรวมไฟล์ C ++ ใช้เวลานานมากเมื่อเทียบกับ C # และ Java การคอมไพล์ไฟล์ C ++ ใช้เวลานานกว่าการรันสคริปต์ Python ขนาดปกติ ขณะนี้ฉันใช้ VC ++ แต่ก็เหมือนกันกับคอมไพเลอร์ใด ๆ ทำไมนี้

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


58
VC ++ รองรับส่วนหัวที่คอมไพล์แล้ว ใช้พวกเขาจะช่วย มาก.
Brian

1
ใช่ในกรณีของฉัน (ส่วนใหญ่ C กับคลาสไม่กี่ - ไม่มีเทมเพลต) ส่วนหัวที่คอมไพล์แล้วเร่งความเร็วได้ประมาณ 10x
Lothar

@ Brian ฉันจะไม่เคยใช้ก่อนรวบรวมหัวในห้องสมุดแม้ว่า
โคลจอห์นสัน

13
It takes significantly longer to compile a C++ file- คุณหมายถึง 2 วินาทีเมื่อเทียบกับ 1 วินาที? แน่นอนว่ามีความยาวสองเท่า แต่มีความสำคัญน้อยมาก หรือคุณหมายถึง 10 นาทีเมื่อเทียบกับ 5 วินาที กรุณาระบุปริมาณ
Nick Gammon

2
ฉันวางเดิมพันของฉันในโมดูล; ฉันไม่คาดหวังว่าโครงการ C ++ จะสามารถสร้างได้เร็วกว่าภาษาโปรแกรมอื่น ๆ ที่ทำกับโมดูล แต่สามารถเข้าใกล้โครงการส่วนใหญ่ได้ด้วยการจัดการบางอย่าง ฉันหวังว่าจะได้เห็นผู้จัดการแพคเกจที่ดีพร้อมการรวมสิ่งประดิษฐ์หลังจากโมดูล
Abdurrahim

คำตอบ:


800

หลากหลายเหตุผล

ไฟล์ส่วนหัว

หน่วยการรวบรวมเดียวทุกหน่วยต้องมีส่วนหัวเป็นร้อยหรือหลายพันเพื่อให้โหลด (1) และ (2) รวบรวม โดยทั่วไปทุก ๆ หน่วยจะต้องทำการคอมไพล์ใหม่สำหรับทุกหน่วยการคอมไพล์เนื่องจากตัวประมวลผลล่วงหน้าทำให้แน่ใจว่าผลลัพธ์ของการรวบรวมส่วนหัวอาจแตกต่างกันระหว่างหน่วยการรวบรวมทั้งหมด (อาจกำหนดแมโครในหนึ่งหน่วยการคอมไพล์ซึ่งเปลี่ยนเนื้อหาของส่วนหัว)

นี่อาจเป็นเหตุผลหลักเนื่องจากต้องมีการรวบรวมรหัสจำนวนมากสำหรับทุกหน่วยการรวบรวมและนอกจากนี้ทุกส่วนหัวจะต้องรวบรวมหลายครั้ง (ครั้งเดียวสำหรับทุกหน่วยการรวบรวมที่มี)

การเชื่อมโยง

รวบรวมเมื่อวัตถุไฟล์ทั้งหมดจะต้องมีการเชื่อมโยงเข้าด้วยกัน นี่เป็นกระบวนการเสาหินที่ไม่สามารถเทียบเคียงได้เป็นอย่างดีและต้องดำเนินการโครงการทั้งหมดของคุณ

วจีวิภาค

ไวยากรณ์มีความซับซ้อนอย่างมากในการแยกวิเคราะห์ขึ้นอยู่กับบริบทและยากที่จะแยกแยะ ใช้เวลานานมาก

แม่แบบ

ใน C # List<T>เป็นประเภทเดียวที่ถูกคอมไพล์ไม่ว่าคุณมีรายการอินสแตนซ์จำนวนเท่าใดในโปรแกรมของคุณ ใน C ++ vector<int>เป็นประเภทที่แยกจากกันโดยสิ้นเชิงvector<float>และแต่ละประเภทจะต้องรวบรวมแยกต่างหาก

เพิ่มไปที่เทมเพลตประกอบขึ้นเป็น "ภาษาย่อย" ที่ทัวริงสมบูรณ์ซึ่งคอมไพเลอร์ต้องตีความและสิ่งนี้อาจซับซ้อนอย่างน่าขัน แม้แต่รหัส metaprogramming แม่แบบที่ค่อนข้างง่ายยังสามารถกำหนดแม่แบบเวียนเกิดที่สร้างอินสแตนซ์ของแม่แบบได้หลายสิบครั้ง เทมเพลตอาจส่งผลให้เกิดประเภทที่ซับซ้อนอย่างยิ่งโดยมีชื่อยาว ๆ น่าขันซึ่งเป็นการเพิ่มงานพิเศษจำนวนมากให้กับลิงเกอร์ (ต้องเปรียบเทียบชื่อสัญลักษณ์จำนวนมากและหากชื่อเหล่านี้สามารถเติบโตได้หลายพันตัวอักษรนั่นอาจมีราคาแพงพอสมควร)

และแน่นอนพวกเขาทำให้ปัญหาไฟล์ส่วนหัวแย่ลงเพราะโดยทั่วไปแม่แบบจะต้องมีการกำหนดไว้ในส่วนหัวซึ่งหมายความว่าจะต้องมีการแยกวิเคราะห์โค้ดและคอมไพล์มากขึ้นสำหรับหน่วยการรวบรวมทุกหน่วย ในรหัส C ธรรมดาส่วนหัวโดยทั่วไปมีเพียงการประกาศไปข้างหน้า แต่รหัสจริงน้อยมาก ใน C ++ ไม่ใช่เรื่องแปลกที่รหัสเกือบทั้งหมดจะอยู่ในไฟล์ส่วนหัว

การเพิ่มประสิทธิภาพ

C ++ ช่วยให้สามารถปรับแต่งได้อย่างน่าทึ่ง C # หรือ Java ไม่อนุญาตให้คลาสถูกกำจัดอย่างสมบูรณ์ (ต้องอยู่ที่นั่นเพื่อจุดประสงค์ในการสะท้อนกลับ) แต่แม้กระทั่งเทมเพลต C ++ เทมเพลต C ++ ที่เรียบง่ายสามารถสร้างคลาสได้อย่างง่ายดายหลายสิบหรือหลายร้อยคลาส ระยะ

ยิ่งไปกว่านั้นโปรแกรม C ++ ต้องได้รับการปรับให้เหมาะสมที่สุดโดยคอมไพเลอร์ โปรแกรม AC # สามารถพึ่งพาคอมไพเลอร์ JIT เพื่อดำเนินการเพิ่มประสิทธิภาพเพิ่มเติม ณ เวลาโหลด C ++ ไม่ได้รับ "โอกาสครั้งที่สอง" สิ่งที่คอมไพเลอร์สร้างนั้นได้รับการปรับให้เหมาะสมที่สุดเท่าที่จะเป็นไปได้

เครื่อง

C ++ ถูกคอมไพล์ไปยังรหัสเครื่องซึ่งอาจจะค่อนข้างซับซ้อนกว่าการใช้ bytecode Java หรือ. NET (โดยเฉพาะในกรณีของ x86) (สิ่งนี้ถูกกล่าวถึงว่าไม่สมบูรณ์เนื่องจากมีการกล่าวถึงในความคิดเห็นและในทางปฏิบัติขั้นตอนนี้ไม่น่าจะใช้เวลานานกว่าการรวบรวมเพียงเล็กน้อย)

ข้อสรุป

ปัจจัยเหล่านี้ส่วนใหญ่ใช้ร่วมกันโดยรหัส C ซึ่งจริง ๆ แล้วรวบรวมอย่างมีประสิทธิภาพ ขั้นตอนการวิเคราะห์คำมีความซับซ้อนมากขึ้นใน C ++ และอาจใช้เวลานานขึ้นอย่างมาก แต่ผู้กระทำผิดหลักน่าจะเป็นแม่แบบ พวกมันมีประโยชน์และทำให้ภาษา C ++ เป็นภาษาที่ทรงพลังยิ่งกว่าเดิม แต่พวกเขายังต้องเสียเวลาในการรวบรวมความเร็ว


38
สำหรับจุดที่ 3: การคอมไพล์ C นั้นเร็วกว่า C ++ เป็นส่วนหน้าแน่นอนที่ทำให้เกิดการชะลอตัวและไม่ใช่การสร้างรหัส
Tom

72
เกี่ยวกับเทมเพลต: เวกเตอร์ <int> ไม่เพียง แต่ต้องรวบรวมแยกจาก vector <double> เท่านั้น แต่ vector <int> จะถูกคอมไพล์ใหม่ในหน่วยการรวบรวมแต่ละหน่วยที่ใช้ ข้อกำหนดซ้ำซ้อนถูกกำจัดโดยตัวเชื่อมโยง
David Rodríguez - dribeas

15
dribeas: จริง แต่นั่นไม่ได้เฉพาะเจาะจงสำหรับเทมเพลต ฟังก์ชั่นอินไลน์หรือสิ่งอื่นใดที่กำหนดไว้ในส่วนหัวจะถูกรวบรวมใหม่ทุกที่รวมอยู่ แต่ใช่ว่าเป็นเรื่องที่เจ็บปวดโดยเฉพาะอย่างยิ่งกับแม่แบบ :)
jalf

15
@configurator: Visual Studio และ gcc ทั้งคู่อนุญาตให้มีส่วนหัวที่คอมไพล์แล้วซึ่งสามารถนำความเร็วที่ร้ายแรงมาสู่การคอมไพล์ได้
small_duck

5
ไม่แน่ใจว่าการเพิ่มประสิทธิภาพเป็นปัญหาหรือไม่เนื่องจาก DEBUG build ของเราช้ากว่าการสร้างโหมด release การสร้าง pdb ยังเป็นตัวการ
gast128

40

การชะลอตัวไม่จำเป็นต้องเหมือนกันกับคอมไพเลอร์ใด ๆ

ฉันไม่ได้ใช้ Delphi หรือ Kylix แต่ย้อนกลับไปในวันที่ MS-DOS โปรแกรม Turbo Pascal จะรวบรวมเกือบทันทีในขณะที่โปรแกรม Turbo C ++ ที่เทียบเท่าจะคลาน

ความแตกต่างที่สำคัญสองประการคือระบบโมดูลที่แข็งแกร่งมากและไวยากรณ์ที่อนุญาตการรวบรวมแบบ Single-Pass

เป็นไปได้อย่างแน่นอนว่าความเร็วในการรวบรวมนั้นไม่ได้มีความสำคัญสำหรับนักพัฒนาคอมไพเลอร์ C ++ แต่ก็มีปัญหาแทรกซ้อนบางอย่างในไวยากรณ์ C / C ++ ที่ทำให้ยากต่อการประมวลผล (ฉันไม่ใช่ผู้เชี่ยวชาญใน C แต่ Walter Bright คือและหลังจากสร้างคอมไพเลอร์ C / C ++ เชิงพาณิชย์หลาย ๆ ตัวเขาได้สร้างภาษา D หนึ่งในการเปลี่ยนแปลงของเขาคือการบังคับใช้ไวยากรณ์แบบไม่มีบริบทเพื่อให้ง่ายต่อการแยกภาษา .)

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


38
มันน่าสนใจที่จะเปรียบเทียบ Pascal เนื่องจาก Niklaus Wirth ใช้เวลาที่ใช้ในการรวบรวมเพื่อเป็นมาตรฐานในการออกแบบภาษาและคอมไพเลอร์ของเขา มีเรื่องราวที่หลังจากเขียนโมดูลอย่างระมัดระวังเพื่อค้นหาสัญลักษณ์อย่างรวดเร็วเขาแทนที่มันด้วยการค้นหาแบบเส้นตรงเนื่องจากขนาดรหัสที่ลดลงทำให้คอมไพเลอร์รวบรวมตัวเองได้เร็วขึ้น
Dietrich Epp

1
@DietrichEpp Empiricism จ่ายออกไป
Tomas Zubiri

39

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

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


14
แน่นอนว่าการเข้าถึงไฟล์มีส่วนร่วมในเรื่องนี้ แต่อย่างที่ jalf กล่าวเหตุผลหลักในการทำเช่นนี้คือการแยกวิเคราะห์ไฟล์ส่วนหัวซ้ำ ๆ จำนวนมากหลาย ๆ ไฟล์ (ซ้อนกัน!) ที่แตกออกในกรณีของคุณอย่างสมบูรณ์
Konrad Rudolph

9
เป็นที่จุดที่เพื่อนของคุณต้องตั้งค่าส่วนหัว precompiled แบ่งการพึ่งพาระหว่างไฟล์ส่วนหัวที่แตกต่างกัน (พยายามหลีกเลี่ยงส่วนหัวหนึ่งรวมถึงอีกส่วนหนึ่งแทนที่จะส่งต่อไปข้างหน้าประกาศ) และรับ HDD ที่เร็วขึ้น นั่นคือเมตริกที่น่าทึ่งทีเดียว
Tom Leys

6
หากไฟล์ส่วนหัวทั้งหมด (ยกเว้นความคิดเห็นที่เป็นไปได้และบรรทัดว่างเปล่า) อยู่ในการ์ดส่วนหัว gcc สามารถจดจำไฟล์และข้ามได้หากกำหนดสัญลักษณ์ที่ถูกต้อง
CesarB

11
การแยกวิเคราะห์เป็นเรื่องใหญ่ สำหรับคู่ซอร์สไฟล์ / ส่วนหัวในทำนองเดียวกันที่มีการพึ่งพาซึ่งกันและกันมี O (N ^ 2) ส่งผ่านไฟล์ส่วนหัว การวางข้อความทั้งหมดลงในไฟล์เดียวคือการตัดคำแยกที่ซ้ำกันออก
Tom

9
หมายเหตุด้านเล็ก ๆ : เจ้าหน้าที่รักษาความปลอดภัยรวมถึงการป้องกันการแยกวิเคราะห์หลายต่อหน่วยรวบรวม ไม่เทียบกับการแยกวิเคราะห์หลายรายการโดยรวม
Marco van de Voort

16

อีกเหตุผลหนึ่งคือการใช้ตัวประมวลผลล่วงหน้า C สำหรับการค้นหาการประกาศ แม้จะมียามหัว, .h ยังคงต้องมีการแยกวิเคราะห์ซ้ำแล้วซ้ำอีกทุกครั้งที่พวกเขารวม คอมไพเลอร์บางตัวรองรับส่วนหัวที่รวบรวมไว้ล่วงหน้าซึ่งสามารถช่วยได้ แต่ก็ไม่ได้ใช้งานเสมอไป

ดูเพิ่มเติม: C ++ คำตอบที่พบบ่อย


ฉันคิดว่าคุณควรแสดงความคิดเห็นต่อส่วนหัวที่คอมไพล์แล้วเพื่อชี้ให้เห็นส่วนสำคัญของคำตอบของคุณ
เควิน

6
หากไฟล์ส่วนหัวทั้งหมด (ยกเว้นความคิดเห็นที่เป็นไปได้และบรรทัดว่างเปล่า) อยู่ในการ์ดส่วนหัว gcc สามารถจดจำไฟล์และข้ามได้หากกำหนดสัญลักษณ์ที่ถูกต้อง
CesarB

5
@CesarB: ยังคงต้องดำเนินการเต็มหนึ่งครั้งต่อหนึ่งหน่วยการรวบรวม (ไฟล์. cpp)
Sam Harwell

16

คอมไพล์ C ++ ถูกคอมไพล์เป็นรหัสเครื่อง ดังนั้นคุณจึงมีตัวประมวลผลล่วงหน้าคอมไพเลอร์ออพติไมเซอร์และสุดท้ายแอสเซมเบลอร์ซึ่งทั้งหมดต้องทำงาน

Java และ C # รวบรวมเป็น byte-code / IL และเครื่องเสมือน Java /. NET Framework ดำเนินการ (หรือรวบรวม JIT เป็นรหัสเครื่อง) ก่อนดำเนินการ

Python เป็นภาษาตีความที่รวบรวมไว้เป็นรหัสไบต์

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


15
ต้นทุนที่เพิ่มขึ้นจากการประมวลผลล่วงหน้านั้นเป็นเรื่องเล็กน้อย "เหตุผลอื่น" ที่สำคัญสำหรับการชะลอตัวคือการรวบรวมจะแบ่งออกเป็นงานที่แยกต่างหาก (หนึ่งต่อไฟล์วัตถุ) ดังนั้นส่วนหัวทั่วไปได้รับการประมวลผลซ้ำแล้วซ้ำอีก นั่นเป็นกรณีที่เลวร้ายที่สุด O (N ^ 2) เทียบกับภาษาอื่น ๆ ส่วนใหญ่ O (N) ในการแยกวิเคราะห์
Tom

12
คุณสามารถบอกได้จากการโต้แย้งเดียวกันว่า C, Pascal และอื่น ๆ คอมไพเลอร์ช้าซึ่งไม่เป็นความจริงโดยเฉลี่ย มันมีอะไรเพิ่มเติมเกี่ยวกับไวยากรณ์ของ C ++ และสถานะอันยิ่งใหญ่ที่คอมไพเลอร์ C ++ ต้องดูแล
เซบาสเตียนมัค

2
C ช้า มันได้รับความทุกข์จากปัญหาการแยกวิเคราะห์ส่วนหัวเช่นเดียวกับวิธีการแก้ปัญหาที่ยอมรับ เช่นใช้โปรแกรม windows GUI อย่างง่ายที่รวม windows.h ในหน่วยการคอมไพล์ไม่กี่ชุดและวัดประสิทธิภาพการคอมไพล์เมื่อคุณเพิ่ม (สั้น) หน่วยการคอมไพล์
Marco van de Voort

13

ปัญหาที่ใหญ่ที่สุดคือ:

1) การวิเคราะห์ส่วนหัวที่ไม่สิ้นสุด พูดถึงแล้ว การบรรเทา (เช่น #pragma หนึ่งครั้ง) มักจะทำงานต่อหน่วยการรวบรวมเท่านั้นไม่ใช่ต่อการสร้าง

2) ความจริงที่ว่า toolchain มักจะถูกแยกออกเป็นหลาย ๆ ไบนารี (ทำ, preprocessor, คอมไพเลอร์, แอสเซมเบลอร์, archiver, impdef, linker และ dlltool ในกรณีที่รุนแรง) ที่ทุกคนต้องเริ่มต้นใหม่และโหลดทุกสถานะตลอดเวลา คอมไพเลอร์แอสเซมเบลอร์) หรือไฟล์ทุกคู่ (archiver, linker และ dlltool)

ดูการสนทนานี้ใน comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078โดยเฉพาะอันนี้

http://compilers.iecc.com/comparch/article/02-07-128

โปรดทราบว่า John ผู้ดำเนินการของ comp.compilers นั้นเห็นด้วยและนั่นหมายความว่ามันควรจะเป็นไปได้ที่จะบรรลุความเร็วที่ใกล้เคียงกันสำหรับ C เช่นกันหากมีการรวม toolchain อย่างสมบูรณ์และใช้ส่วนหัวที่คอมไพล์แล้วล่วงหน้า คอมไพเลอร์ C เชิงพาณิชย์จำนวนมากทำสิ่งนี้ในระดับหนึ่ง

โปรดทราบว่ารูปแบบ Unix ของการแยกตัวประกอบทุกอย่างออกเป็นไบนารีแยกเป็นรูปแบบตัวพิมพ์ที่แย่ที่สุดสำหรับ Windows (ด้วยการสร้างกระบวนการที่ช้า) เป็นสิ่งที่น่าสังเกตมากเมื่อเปรียบเทียบเวลาในการสร้าง GCC ระหว่าง Windows กับ * ระวังโดยเฉพาะอย่างยิ่งหากระบบ make / configure เรียกใช้บางโปรแกรมเพื่อรับข้อมูล


12

อาคาร C / C ++: เกิดอะไรขึ้นจริงและทำไมมันใช้เวลานานมาก

เวลาส่วนใหญ่ในการพัฒนาซอฟต์แวร์ไม่ได้ถูกใช้ในการเขียน, รัน, ดีบั๊กหรือแม้กระทั่งการออกแบบรหัส แต่รอให้การคอมไพล์เสร็จสิ้น ก่อนอื่นเราต้องเข้าใจว่าเกิดอะไรขึ้นเมื่อคอมไพล์ซอฟต์แวร์ C / C ++ ขั้นตอนคร่าว ๆ ดังนี้:

  • องค์ประกอบ
  • สร้างเครื่องมือเริ่มต้น
  • การตรวจสอบการพึ่งพา
  • การรวบรวม
  • การเชื่อมโยง

ตอนนี้เราจะพิจารณาแต่ละขั้นตอนอย่างละเอียดโดยเน้นที่วิธีการที่จะทำให้เร็วขึ้น

องค์ประกอบ

นี่เป็นขั้นตอนแรกเมื่อเริ่มสร้าง โดยปกติแล้วหมายถึงการรันสคริปต์กำหนดค่าหรือ CMake, Gyp, SCons หรือเครื่องมืออื่น ๆ สิ่งนี้อาจใช้เวลาตั้งแต่หนึ่งวินาทีถึงหลายนาทีในการกำหนดค่าสคริปต์ที่ใช้ Autotools ขนาดใหญ่มาก

ขั้นตอนนี้เกิดขึ้นค่อนข้างน้อย จำเป็นต้องรันเมื่อเปลี่ยนการกำหนดค่าหรือเปลี่ยนการกำหนดค่าการสร้าง สั้น ๆ ของการเปลี่ยนแปลงระบบสร้างมีไม่มากที่ต้องทำเพื่อให้ขั้นตอนนี้เร็วขึ้น

สร้างเครื่องมือเริ่มต้น

นี่คือสิ่งที่เกิดขึ้นเมื่อคุณเรียกใช้ make หรือคลิกที่ไอคอนบิลด์บน IDE (ซึ่งมักจะเป็นนามแฝงสำหรับสร้าง) เครื่องมือสร้างไบนารีเริ่มต้นและอ่านไฟล์การกำหนดค่ารวมถึงการกำหนดค่าการสร้างซึ่งมักจะเป็นสิ่งเดียวกัน

ขึ้นอยู่กับความซับซ้อนและขนาดของการสร้างสิ่งนี้สามารถทำได้ทุกที่ตั้งแต่เสี้ยววินาทีถึงหลายวินาที ด้วยตัวเองนี้จะไม่เลวร้าย น่าเสียดายที่ระบบการสร้างที่อิงตามระบบส่วนใหญ่เป็นสาเหตุทำให้ต้องมีการเรียกใช้หลายสิบถึงร้อยเท่าของทุกบิลด์ โดยปกติจะเกิดจากการใช้ยี่ห้อซ้ำ (ซึ่งไม่ดี)

ควรสังเกตว่าเหตุผลที่ทำให้ช้ามากไม่ใช่ข้อผิดพลาดในการใช้งาน ไวยากรณ์ของ Makefiles มีนิสัยใจคอบางอย่างที่ทำให้การติดตั้งใช้งานได้รวดเร็ว แต่ก็เป็นไปไม่ได้ ปัญหานี้จะเห็นได้ชัดเจนยิ่งขึ้นเมื่อรวมกับขั้นตอนถัดไป

การตรวจสอบการพึ่งพา

เมื่อเครื่องมือสร้างอ่านการกำหนดค่าแล้วมันจะต้องพิจารณาว่าไฟล์ใดที่มีการเปลี่ยนแปลงและไฟล์ใดที่ต้องทำการคอมไพล์ใหม่ ไฟล์การกำหนดค่ามีกราฟ acyclic กำกับที่อธิบายถึงการพึ่งพาการสร้าง โดยปกติแล้วกราฟนี้จะถูกสร้างขึ้นในระหว่างขั้นตอนการกำหนดค่า เวลาเริ่มต้นเครื่องมือสร้างและสแกนเนอร์อ้างอิงจะทำงานในทุกบิลด์เดียว รันไทม์ที่รวมกันของพวกเขาจะกำหนดขอบเขตที่ต่ำกว่าในวงจรการแก้ไข สำหรับโครงการขนาดเล็กเวลานี้มักจะไม่กี่วินาทีหรือดังนั้น สิ่งนี้ทนได้ มีทางเลือกในการทำ เร็วที่สุดคือ Ninja ซึ่งสร้างโดยวิศวกร Google สำหรับ Chromium หากคุณใช้ CMake หรือ Gyp เพื่อสร้างเพียงสลับไปที่แบ็กเอนด์นินจาของพวกเขา คุณไม่จำเป็นต้องเปลี่ยนแปลงอะไรในไฟล์บิลด์ด้วยตัวเองเพียงแค่สนุกกับการเพิ่มความเร็ว Ninja ไม่ได้บรรจุในการแจกแจงส่วนใหญ่

การรวบรวม

ณ จุดนี้ในที่สุดเราก็เรียกคอมไพเลอร์ การตัดมุมบางส่วนนี่คือขั้นตอนโดยประมาณ

  • การผสานรวม
  • การแยกรหัส
  • การสร้างรหัส / การปรับให้เหมาะสม

ตรงกันข้ามกับความเชื่อที่ได้รับความนิยมการรวบรวม C ++ ไม่ใช่สิ่งที่ช้าอย่างแท้จริง STL ช้าและเครื่องมือสร้างส่วนใหญ่ที่ใช้ในการคอมไพล์ C ++ นั้นช้า อย่างไรก็ตามมีเครื่องมือและวิธีการที่รวดเร็วกว่าในการลดส่วนที่ช้าของภาษา

การใช้มันต้องใช้จาระบีข้อศอกเล็กน้อย แต่ข้อดีก็คือไม่สามารถปฏิเสธได้ เวลาในการสร้างที่เร็วขึ้นนำไปสู่นักพัฒนาที่มีความสุขความคล่องตัวมากขึ้นและในที่สุดรหัสที่ดีกว่า


9

ภาษาที่รวบรวมจะต้องใช้ค่าเริ่มต้นที่ใหญ่กว่าภาษาที่ตีความ นอกจากนี้บางทีคุณอาจไม่ได้จัดโครงสร้างโค้ด C ++ ของคุณไว้อย่างดี ตัวอย่างเช่น:

#include "BigClass.h"

class SmallClass
{
   BigClass m_bigClass;
}

รวบรวมช้ากว่ามาก:

class BigClass;

class SmallClass
{
   BigClass* m_bigClass;
}

3
โดยเฉพาะอย่างยิ่งหาก BigClass เกิดขึ้นเพื่อรวมไฟล์อีก 5 ไฟล์ที่ใช้ในที่สุดรวมถึงรหัสทั้งหมดในโปรแกรมของคุณ
Tom Leys

7
นี่อาจเป็นเหตุผลหนึ่ง ตัวอย่างเช่น Pascal ใช้เวลาเพียงหนึ่งในสิบของเวลาในการคอมไพล์ที่เทียบเท่ากับโปรแกรม C ++ นี่ไม่ใช่เพราะการเพิ่มประสิทธิภาพ gcc: s ใช้เวลานาน แต่ Pascal นั้นง่ายต่อการวิเคราะห์และไม่ต้องจัดการกับ preprocessor โปรดดูคอมไพเลอร์ Digital Mars D
Daniel O

2
ไม่ใช่การแยกวิเคราะห์ที่ง่ายขึ้นมันเป็นโมดูลที่หลีกเลี่ยงการแปล windows.h และขยายส่วนหัวอื่น ๆ สำหรับการรวบรวมแต่ละหน่วย ใช่ภาษา Pascal แยกวิเคราะห์ง่ายกว่า (แม้ว่าจะเป็นผู้ใหญ่เช่น Delphi จะซับซ้อนกว่าเดิม) แต่นั่นไม่ใช่สิ่งที่ทำให้เกิดความแตกต่างใหญ่โต
Marco van de Voort

1
เทคนิคการแสดงที่นี่ซึ่งมีการปรับปรุงความเร็วในการสะสมเป็นที่รู้จักกันประกาศไปข้างหน้า
DavidRR

เขียนคลาสในไฟล์เดียว มันจะไม่เป็นรหัสยุ่งไหม?
Fennekin

7

วิธีง่ายๆในการลดเวลาการคอมไพล์ในโปรเจ็กต์ C ++ ที่ใหญ่กว่าคือการทำให้ * .cpp รวมไฟล์ที่รวมไฟล์ cpp ทั้งหมดในโปรเจ็กต์ของคุณและคอมไพล์มัน สิ่งนี้จะช่วยลดปัญหาการระเบิดส่วนหัวให้เป็นหนึ่งครั้ง ข้อดีของการทำเช่นนี้คือข้อผิดพลาดในการรวบรวมจะยังคงอ้างอิงไฟล์ที่ถูกต้อง

ตัวอย่างเช่นสมมติว่าคุณมี a.cpp, b.cpp และ c.cpp .. สร้างไฟล์: everything.cpp:

#include "a.cpp"
#include "b.cpp"
#include "c.cpp"

จากนั้นรวบรวมโครงการโดยทำทุกอย่าง


3
ฉันไม่เห็นการคัดค้านวิธีนี้ สมมติว่าคุณสร้างการรวมจากสคริปต์หรือ Makefile ไม่ใช่ปัญหาการบำรุงรักษา ในความเป็นจริงมันเร็วขึ้นรวบรวมโดยไม่ทำให้งงปัญหาการรวบรวม คุณสามารถโต้แย้งการใช้หน่วยความจำในการรวบรวม แต่นั่นไม่ค่อยมีปัญหาในเครื่องที่ทันสมัย ดังนั้นวัตถุสำหรับวิธีนี้คืออะไร (นอกเหนือจากการยืนยันว่ามันผิด)?
rileyberton

9
@rileyberton (เนื่องจากมีใครบางคนโหวตความคิดเห็นของคุณ) ให้ฉันสะกดมันออกมา: ไม่มีมันไม่ได้รวบรวมความเร็ว ในความเป็นจริงมันทำให้แน่ใจว่าการคอมไพล์ใด ๆ ใช้เวลาสูงสุดโดยไม่แยกหน่วยการแปล สิ่งที่ดีเกี่ยวกับพวกเขาคือคุณไม่จำเป็นต้องคอมไพล์ใหม่ทั้งหมด. cpp-s หากพวกเขาไม่เปลี่ยน (นั่นไม่คำนึงถึงข้อโต้แย้งโวหาร) การจัดการการพึ่งพาที่เหมาะสมและส่วนหัวที่คอมไพล์แล้วอาจดีกว่ามาก
sehe

7
ขออภัยนี่อาจเป็นวิธีที่มีประสิทธิภาพมากในการเร่งการรวบรวมเนื่องจากคุณ (1) กำจัดการเชื่อมโยงค่อนข้างมากและ (2) ต้องดำเนินการกับส่วนหัวที่ใช้กันทั่วไปเพียงครั้งเดียว นอกจากนี้ยังใช้งานได้จริงหากคุณไม่อยากลอง น่าเสียดายที่มันทำให้การสร้างใหม่เป็นไปไม่ได้ที่เพิ่มขึ้นดังนั้นการสร้างทุกครั้งจึงเริ่มต้นขึ้นใหม่ทั้งหมด แต่เต็มสร้างด้วยวิธีนี้เป็นจำนวนมากได้เร็วขึ้นกว่าสิ่งที่คุณจะได้รับอย่างอื่น
jalf

4
@BartekBanachewicz แน่นอน แต่สิ่งที่คุณพูดคือ "มันไม่ได้เพิ่มความเร็วในการรวบรวม" โดยไม่มีการคัดเลือก ดังที่คุณกล่าวว่ามันทำให้การคอมไพล์ทุกครั้งใช้เวลาสูงสุด (ไม่มีการสร้างใหม่บางส่วน) แต่ในเวลาเดียวกันมันจะลดจำนวนสูงสุดอย่างมากเมื่อเทียบกับสิ่งที่มันเป็น ฉันแค่บอกว่ามันเป็นบิตที่เหมาะสมยิ่งขึ้นกว่า "ไม่ทำเช่นนี้"
jalf

2
ขอให้สนุกกับตัวแปรและฟังก์ชั่นคงที่ หากฉันต้องการหน่วยรวบรวมขนาดใหญ่ฉันจะสร้างไฟล์. cpp ขนาดใหญ่
gnasher729

6

เหตุผลบางประการคือ:

1) ไวยากรณ์ C ++ นั้นซับซ้อนกว่า C # หรือ Java และใช้เวลาในการวิเคราะห์มากขึ้น

2) (สำคัญกว่า) คอมไพเลอร์ C ++ สร้างรหัสเครื่องและทำการปรับแต่งทั้งหมดในระหว่างการรวบรวม C # และ Java ไปครึ่งทางและออกจากขั้นตอนเหล่านี้ไปที่ JIT


5

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


3

คำตอบส่วนใหญ่ยังไม่ชัดเจนในการกล่าวถึงว่า C # จะทำงานช้าลงเนื่องจากต้นทุนของการดำเนินการที่ใน C ++ ดำเนินการเพียงครั้งเดียวในเวลารวบรวมค่าใช้จ่ายประสิทธิภาพการทำงานนี้ยังได้รับผลกระทบเนื่องจากการพึ่งพารันไทม์ เพื่อเรียกใช้) ไม่ต้องพูดถึงว่าโปรแกรม C # จะมีหน่วยความจำสูงกว่าเสมอซึ่งส่งผลให้ประสิทธิภาพมีความสัมพันธ์อย่างใกล้ชิดกับความสามารถของฮาร์ดแวร์ที่มีให้ เช่นเดียวกับภาษาอื่นที่ตีความหรือพึ่งพา VM


3

มีสองประเด็นที่ฉันคิดว่าอาจมีผลต่อความเร็วที่โปรแกรมของคุณใน C ++ รวบรวม

ปัญหาที่เป็นไปได้ # 1 - การรวบรวม HEADER: (นี่อาจหรืออาจไม่ได้รับการแก้ไขแล้วโดยคำตอบหรือความคิดเห็นอื่น) Microsoft Visual C ++ (AKA VC ++) สนับสนุนส่วนหัวที่คอมไพล์แล้วซึ่งฉันขอแนะนำเป็นอย่างยิ่ง เมื่อคุณสร้างโครงการใหม่และเลือกประเภทของโปรแกรมที่คุณทำหน้าต่างตัวช่วยสร้างการตั้งค่าควรปรากฏขึ้นบนหน้าจอของคุณ หากคุณกดปุ่ม "ถัดไป>" ที่ด้านล่างหน้าต่างจะนำคุณไปยังหน้าที่มีรายการคุณลักษณะหลายอย่าง ตรวจสอบให้แน่ใจว่าได้ทำเครื่องหมายในช่องถัดจากตัวเลือก“ ส่วนหัวที่คอมไพล์แล้ว” (หมายเหตุ: นี่เป็นประสบการณ์ของฉันกับแอปพลิเคชันคอนโซลของ Win32 ใน C ++ แต่อาจไม่เป็นกรณีของโปรแกรมทุกชนิดใน C ++)

ปัญหาที่เป็นไปได้ # 2 - สถานที่ตั้งรวบรวม:ฤดูร้อนนี้ฉันเรียนหลักสูตรการเขียนโปรแกรมและเราต้องจัดเก็บโครงการทั้งหมดของเราในแฟลชไดรฟ์ 8GB เนื่องจากคอมพิวเตอร์ในห้องปฏิบัติการที่เราใช้ถูกเช็ดทุกคืนในเวลาเที่ยงคืน ซึ่งจะลบงานทั้งหมดของเรา หากคุณกำลังรวบรวมอุปกรณ์เก็บข้อมูลภายนอกเพื่อประโยชน์ในการพกพา / ความปลอดภัย / ฯลฯ อาจใช้เวลานานมากเวลา (แม้จะมีส่วนหัวที่คอมไพล์แล้วที่ฉันได้กล่าวไว้ข้างต้น) เพื่อให้โปรแกรมของคุณรวบรวมโดยเฉพาะอย่างยิ่งหากเป็นโปรแกรมที่ค่อนข้างใหญ่ คำแนะนำของฉันสำหรับคุณในกรณีนี้คือการสร้างและรวบรวมโปรแกรมบนฮาร์ดไดรฟ์ของคอมพิวเตอร์ที่คุณใช้และเมื่อใดก็ตามที่คุณต้องการ / จำเป็นต้องหยุดทำงานในโครงการของคุณด้วยเหตุผลใดก็ตามให้โอนไปยังภายนอกของคุณ อุปกรณ์จัดเก็บข้อมูลจากนั้นคลิกไอคอน“ Safely Remove Hardware และ Eject Media” ซึ่งควรปรากฏเป็นแฟลชไดรฟ์ขนาดเล็กด้านหลังวงกลมสีเขียวเล็ก ๆ ที่มีเครื่องหมายสีขาวติดอยู่เพื่อยกเลิกการเชื่อมต่อ

ฉันหวังว่านี่จะช่วยคุณได้ แจ้งให้เราทราบหากมัน! :)

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