C++ Modules เผชิญเสียงเรียกร้องให้ถอดออกจากมาตรฐานหลังจากปีที่ผ่านมาต้องดิ้นรนกับการใช้งาน

ทีมชุมชน BigGo
C++ Modules เผชิญเสียงเรียกร้องให้ถอดออกจากมาตรฐานหลังจากปีที่ผ่านมาต้องดิ้นรนกับการใช้งาน

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

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

คำสัญญาด้านประสิทธิภาพที่ไม่เคยเป็นจริง

เมื่อ C++ modules ถูกเสนอครั้งแรก จุดขายหลักนั้นชัดเจน คือการปรับปรุงความเร็วการคอมไพล์อย่างมหาศาล ระบบการรวม header แบบดั้งเดิมสร้างอัลกอริทึม O(N²) ที่โค้ดเดียวกันถูกแยกวิเคราะห์ซ้ำๆ ในไฟล์ source หลายไฟล์ Modules ควรจะแก้ไขปัญหานี้โดยการเก็บโค้ดที่ผ่านการประมวลผลไว้ในรูปแบบไบนารีที่สามารถโหลดจากดิสก์ได้อย่างรวดเร็ว

อย่างไรก็ตาม การทดสอบในโลกแห่งความเป็นจริงเผยให้เห็นเรื่องราวที่แตกต่างออกไป การใช้งานปัจจุบันแสดงให้เห็นการปรับปรุงเพียงเล็กน้อยที่ 10-20% ในกรณีที่ดีที่สุด ซึ่งห่างไกลจากผลประโยชน์ที่เปลี่ยนแปลงโฉมหน้าที่สัญญาไว้ในตอนแรก สมาชิกในชุมชนบางคนได้ค้นพบว่าเทคนิคที่มีอยู่แล้ว เช่น standard libraries ที่ออกแบบอย่างระมัดระวัง สามารถทำให้ความเร็วการคอมไพล์เพิ่มขึ้น 4 เท่าได้โดยไม่ต้องใช้ความซับซ้อนที่ modules นำมา

สถานการณ์จะน่าเป็นห่วงมากขึ้นเมื่อพิจารณาว่า precompiled headers ซึ่งเป็นเทคโนโลยีที่เก่ากว่ามาก มักจะให้ผลประโยชน์ด้านประสิทธิภาพที่คล้ายกันหรือดีกว่าด้วยความซับซ้อนในการใช้งานที่น้อยกว่ามาก สิ่งนี้ทำให้เกิดคำถามพื้นฐานว่าความพยายามทางวิศวกรรมขนาดใหญ่ที่ลงทุนใน modules นั้นคุ้มค่าหรือไม่

ประสิทธิภาพของโมดูลปัจจุบันเทียบกับความคาดหวัง

  • คำสัญญาเดิม: เพิ่มความเร็วในการคอมไพล์ 5-10 เท่า
  • ความเป็นจริงปัจจุบัน: ปรับปรุงได้เพียง 10-20% ในกรณีที่ดีที่สุด
  • Precompiled Headers: มักให้ประสิทธิภาพเทียบเท่าหรือดีกว่า
  • โซลูชันทางเลือก: ZapCC ทำความเร็วได้มากกว่า 5 เท่าโดยใช้เทคโนโลยีที่มีอยู่

ความวุ่นวายในการใช้งานระหว่าง Compilers

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

ความท้าทายในการรวมระบบนั้นรุนแรงมากจนระบบ build ต้องสร้าง compiler flags เพิ่มเติมระหว่างการคอมไพล์ เก็บไว้ในไฟล์ชั่วคราว และส่งต่อไปยังคำสั่งการคอมไพล์ที่ตามมา ระดับความซับซ้อนนี้ตรงข้ามกับความเรียบง่ายที่สง่างามที่ modules ควรจะนำมาสู่การพัฒนา C++

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

การขาด product owner ที่เป็นหนึ่งเดียวที่มีอำนาจเหนือส่วนที่เคลื่อนไหวทั้งหมดได้สร้างสิ่งที่หลายคนอธิบายว่าเป็นฝันร้ายแบบ kafkaesque ของความซับซ้อน หากไม่มีใครที่ได้รับอำนาจในการประสานงานระหว่างทีม compiler ผู้ดูแล build system และผู้ใช้งาน standard library modules จะยังคงติดอยู่ในสถานะของการทำงานบางส่วน

ความท้าทายหลักในการนำไปใช้งาน

  • การแยกส่วนของ Compiler: แต่ละผู้ผลิตนำ modules ไปใช้งานแตกต่างกัน
  • การรวมระบบ Build: ต้องการการจัดการไฟล์ชั่วคราวที่ซับซ้อน
  • ปัญหาความสามารถในการพกพา: ไฟล์ binary ของ module ไม่สามารถพกพาระหว่าง compilers ได้ (ยกเว้น MSVC )
  • การสนับสนุน Toolchain: การสนับสนุน module ของ Apple ยังคงระบุว่า "บางส่วน"
  • โค้ดเก่า: ไม่สามารถผสม include <vector> และ import <vector> ในโปรเจกต์เดียวกันได้

การเรียนรู้จากแนวทางทางเลือก

การอภิปรายของชุมชนได้เน้นแนวทางทางเลือกหลายแนวทางที่อาจจะประสบความสำเร็จมากกว่า นักพัฒนาบางคนชี้ไปที่ภาษาโปรแกรม D ซึ่งใช้งานระบบ module ที่สะอาดเมื่อกว่าสองทศวรรษที่แล้วและยังคงทำงานได้อย่างน่าเชื่อถือ แนวทางของ D รวมถึงฟีเจอร์เช่น closed namespaces และ semantic independence ที่ทำให้ modules แยกตัวและคาดเดาได้อย่างแท้จริง

ข้อเสนอแนะอื่นๆ มุ่งเน้นไปที่โซลูชันที่เรียบง่ายกว่าที่สามารถให้ประโยชน์ส่วนใหญ่ด้วยความซับซ้อนที่น้อยกว่ามาก คำสั่ง import ที่ตรงไปตรงมาที่ทำงานเหมือน include แต่ไม่มีการรั่วไหลของ context สามารถใช้งานได้ง่ายกว่ามากในขณะที่ยังคงเปิดใช้งานการปรับปรุง compiler ที่สำคัญ

เครื่องมือเช่น ZapCC ซึ่งบรรลุความเร็วการคอมไพล์ที่เพิ่มขึ้น 5 เท่าขึ้นไปผ่านกระบวนการ compiler ที่ต่อเนื่อง แสดงให้เห็นว่าการปรับปรุงเวลา build อย่างมากเป็นไปได้แล้วโดยใช้เทคโนโลยีที่มีอยู่ โซลูชันเหล่านี้ถูกละเลยเป็นส่วนใหญ่เพื่อสนับสนุนแนวทาง modules ที่ซับซ้อนกว่า

เส้นทางข้างหน้า

เมื่อการถกเถียงทวีความรุนแรงขึ้น ผลลัพธ์ที่เป็นไปได้หลายแบบกำลังถูกหารือกัน บางคนโต้แย้งว่าควรถอด modules ออกจากมาตรฐานทั้งหมดและเริ่มใหม่ด้วยแนวทางที่เรียบง่ายกว่า คนอื่นๆ แนะนำให้มุ่งเน้นไปที่ subset import std ซึ่งสามารถให้ประโยชน์ที่มีความหมายสำหรับการใช้งาน standard library โดยไม่ต้องใช้ความซับซ้อนทั้งหมดของ modules ทั่วไป

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

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

อ้างอิง: Nibble Stew