คอมไพเลอร์รวบรวมไฟล์ส่วนหัวที่รวมไว้ทั้งหมดพร้อมกับโปรแกรมหลักทุกครั้งที่เราคอมไพล์โปรแกรมนั้นหรือไม่?

ตาม Wikipedia นี่คือสิ่งที่ C preprocessor ทำ:

"ตัวประมวลผลล่วงหน้าจะแทนที่บรรทัด #include <stdio.h> ด้วยข้อความของไฟล์ 'stdio.h' ซึ่งประกาศฟังก์ชัน printf() เหนือสิ่งอื่นใด"

แล้วถ้าเป็นเช่นนั้นจริง โปรแกรมที่มีไฟล์ส่วนหัวมากกว่านั้นจะใช้เวลาในการคอมไพล์นานขึ้นใช่หรือไม่


person user3125702    schedule 21.12.2013    source แหล่งที่มา
comment
โปรดทราบว่าโดยปกติตรรกะจะไม่เข้าไปในไฟล์ .h มันมีค่าคงที่ คลาส และคำจำกัดความของฟังก์ชัน แต่โค้ดจะอยู่ในไฟล์ .c หรือ .cpp (ซึ่งไม่ได้คอมไพล์โดยอัตโนมัติ)   -  person SJuan76    schedule 21.12.2013
comment
คำจำกัดความของฟังก์ชันไม่ได้หมายถึงตรรกะใช่ไหม   -  person ajay    schedule 21.12.2013
comment
@ ajay แย่แล้ว ฉันควรจะพูดประกาศ   -  person SJuan76    schedule 21.12.2013
comment
@ SJuan76 ลองอ่านส่วนหัว STL   -  person SK-logic    schedule 22.12.2013


คำตอบ (3)


แล้วถ้าเป็นเช่นนั้นจริง โปรแกรมที่มีไฟล์ส่วนหัวมากกว่านั้นจะใช้เวลาในการคอมไพล์นานขึ้นใช่หรือไม่

แน่นอน. พูดอย่างเคร่งครัด ยิ่งคอมไพเลอร์ต้องดูโค้ดมากเท่าไร ก็ยิ่งต้องใช้เวลาในการประมวลผลมากขึ้นเท่านั้น สำหรับโปรเจ็กต์ขนาดใหญ่บางโปรเจ็กต์ อาจเป็นเรื่องที่ต้องคำนึงถึงระยะเวลาในการดูไฟล์ทั้งหมดได้อย่างง่ายดาย นี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งสำหรับโค้ดเทมเพลตที่มีขนาดใหญ่เป็นพิเศษและ/หรือซับซ้อน ซึ่งด้วยเหตุผลในทางปฏิบัติ จะต้องอยู่ในไฟล์ส่วนหัว การจัดระเบียบไฟล์ส่วนหัวยังส่งผลต่อเวลาในการคอมไพล์ด้วย

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

ตัวอย่างเช่น GCC รับรู้เป็นพิเศษว่ารวม guards เพื่อลดเวลาในการประมวลผล และคอมไพเลอร์ทุกวันนี้เริ่มดีขึ้นมากในการจัดการโค้ดเทมเพลตที่ซับซ้อนเช่น ห้องสมุดมาตรฐานส่วนใหญ่ บนคอมไพเลอร์ VC++ รวมถึง windows.h (ซึ่งมีฟังก์ชันต้นแบบสำหรับ Windows API ทั้งหมด) ไม่ได้เพิ่มเวลาในการคอมไพล์มากนักจากประสบการณ์ของฉัน และหากวิธีอื่นล้มเหลว คอมไพเลอร์หลายตัวหากไม่ใช่ทุกตัวจะมีคุณลักษณะ "ส่วนหัวที่คอมไพล์แล้ว" ที่คุณสามารถใช้ได้ .

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

person In silico    schedule 21.12.2013

โดยทั่วไปแล้ว ใช่ โปรแกรมที่มีไฟล์ส่วนหัวมากกว่าปกติจะใช้เวลาในการคอมไพล์นานกว่า


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

เทคนิคทั่วไปอย่างหนึ่งที่ควรหลีกเลี่ยงซึ่งเรียกว่า ส่วนหัวที่คอมไพล์แล้ว

เท่าที่ฉันรู้เทคนิคที่ไม่ธรรมดาซึ่งอาจไม่ได้ใช้โดยคอมไพเลอร์ใด ๆ คือการอนุญาตให้โปรแกรมเมอร์พูดว่า "สำหรับการคอมไพล์ชุดนี้คุณสามารถสรุปได้ว่าส่วนหัวทั้งหมดจะขยายเป็นแบบเดียวกัน" และอาจมีสิ่งนั้นเป็น < em>default (แม้ว่าจะให้ผลลัพธ์ที่แตกต่างจาก C++ มาตรฐานเมื่อสมมติฐานไม่เป็นไปตามนั้น)

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


David Vandevoorde ทำงานใน โมดูล ข้อเสนอ สำหรับ C ++

ตัวอย่างภาษาที่มีโมดูล: Modula-2, Ada, UCSD Pascal

น่าเสียดายที่ยังไม่พร้อมสำหรับ C++11 แต่บางทีเราอาจจะได้รับโมดูลในภายหลัง

person Cheers and hth. - Alf    schedule 21.12.2013

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

ในทางปฏิบัติแล้วจะขึ้นอยู่กับสิ่งที่คุณรวมไว้และอย่างไร หากคุณรวมไว้ในวิธีที่เหมาะสม ผลกระทบต่อความเร็วก็สามารถละเลยได้ การรวมส่วนหัวมาตรฐานมากเกินไปจะทำให้เกิดมลพิษทางเนม-สเปซค่อนข้างมาก จากนั้นจึงเกิดปัญหาเวลาคอมไพล์ โค้ดทั้งหมดของไลบรารีมาตรฐานได้รับการคอมไพล์แล้วและจะลิงก์เท่านั้น ใน Qt คุณสามารถรวมแต่ละคลาสแยกกัน (เช่น #include <QWidget>) หรือ - ความเป็นไปได้สำหรับโปรแกรมเมอร์ขี้เกียจ - รวมคลาสทั้งหมดจากโมดูล (เช่น #include <QtGui>) ซึ่งประกอบด้วยส่วนหัว พัน ดังนั้นฉันไม่เคยสังเกตเห็นความแตกต่างด้านเวลาการคอมไพล์ระหว่างสองวิธีมากนัก

ความคิดและข้อเท็จจริงบางประการจากประสบการณ์ของฉัน:

  • ในซิลิโก กล่าวถึงการป้องกันส่วนหัว โดยหลักแล้วไม่ได้มีไว้สำหรับการเพิ่มประสิทธิภาพเวลาคอมไพล์ แต่เพื่อป้องกันการรวมแบบวงกลม ดังนั้นหากคุณรวมส่วนหัวไว้ใน 10 ตำแหน่ง ส่วนหัวนั้นจะถูกแยกวิเคราะห์เพียงครั้งเดียว

  • ใน C ++ จะรวมไว้ในส่วนหัวเฉพาะในกรณีที่คุณได้รับมาจากคลาสในส่วนหัวนั้น มิฉะนั้นให้ใช้การประกาศส่งต่อ

  • ใช้สำนวนการใช้งานส่วนตัว - ดังนั้นส่วนหัวของคุณจะมีพอยน์เตอร์เพียงตัวเดียวหรือสองสามตัว

  • อย่าใส่ส่วนหัวของโค้ดที่คุณไม่ต้องการในไฟล์ c/cpp นั้น ระบบบิลด์ที่ดีจะวิเคราะห์การขึ้นต่อกันของส่วนหัวตามคำสั่งรวม รหัสของคุณจะถูกคอมไพล์ใหม่โดยไม่จำเป็นทุกครั้งที่มีการเปลี่ยนแปลงส่วนหัวที่รวมอยู่ด้วย

  • นอกจากเวลาในการรวบรวมแล้ว เวลาในการเชื่อมโยงก็มีความสำคัญเช่นกัน ตรวจสอบให้แน่ใจว่าแอปพลิเคชันประกอบด้วย DLL (ไฟล์ DLL/so) ดังนั้นคุณจึงสามารถหลีกเลี่ยงการเชื่อมโยงทั้งหมดเข้าด้วยกันในการคอมไพล์แต่ละครั้ง ในโปรเจ็กต์หนึ่ง ฉันใช้เวลาในการเชื่อมโยงประมาณ 10 นาที ลูกค้าปฏิเสธที่จะใช้ DLL เนื่องจากเหตุผลลึกลับบางประการ

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

  • ในโปรเจ็กต์หนึ่ง เวลาในการสร้างเสร็จสมบูรณ์ลดลงจาก 4 ชั่วโมงเหลือ 40 นาที หลังจากทำการปรับเปลี่ยนเครื่องมือสร้างบางส่วน หากคุณใช้ Linux ให้ตรวจสอบ strace ว่า make ทำงานอย่างไร คุณจะประหลาดใจว่ามีการเข้าถึงไฟล์ที่ไม่จำเป็นมากเพียงใด

person Valentin Heinitz    schedule 21.12.2013