compiler backend ตัวใหม่ที่เรียกว่า TPDE-LLVM ได้ถูกเปิดเป็น open-source แล้ว โดยสัญญาว่าจะเพิ่มความเร็วในการคอมไพล์อย่างมากในขณะที่ยังคงประสิทธิภาพ runtime ที่คล้ายคลึงกับ LLVM standard -O0 backend โปรเจกต์นี้แก้ไขหนึ่งในข้อร้องเรียนที่มีมาอย่างต่อเนื่องเกี่ยวกับ LLVM นั่นคือความเร็วในการคอมไพล์ที่ช้า โดยเฉพาะสำหรับ debug builds
ความเร็วที่น่าประทับใจพร้อมกับข้อแลกเปลี่ยน
TPDE-LLVM ให้ความเร็วในการคอมไพล์ที่เร็วกว่า LLVM -O0 backend ถึง 10-20 เท่าในการทดสอบต่างๆ การทดสอบบน SPEC CPU 2017 แสดงให้เห็นการปรับปรุงที่สม่ำเสมอ โดยโปรแกรมบางตัวเช่น omnetpp และ xalanc มีการคอมไพล์ที่เร็วกว่า 20 เท่า อย่างไรก็ตาม ความเร็วนี้มาพร้อมกับต้นทุนของขนาดโค้ดที่ใหญ่ขึ้นเล็กน้อย โดยทั่วไปจะใหญ่กว่า standard LLVM output ประมาณ 10-30%
ชุมชนมีปฏิกิริยาที่หลากหลายต่อผลลัพธ์เหล่านี้ ในขณะที่นักพัฒนาบางคนตื่นเต้นกับ debug builds ที่เร็วขึ้น คนอื่นๆ ก็ชี้ให้เห็นข้อจำกัดที่สำคัญ backend ปัจจุบันรองรับเพียงส่วนย่อยของ LLVM intermediate representation (IR) และมีเป้าหมายเฉพาะ x86-64 และ AArch64 processors เท่านั้น
IR (Intermediate Representation): ภาษาโปรแกรมระดับต่ำที่ใช้ภายในโดย compilers เพื่อแสดง source code ก่อนแปลงเป็น machine code
ผลลัพธ์ประสิทธิภาพ SPEC CPU 2017 (x86-64)
Benchmark | ความเร็วในการ Compile เพิ่มขึ้น (-O0) | อัตราส่วนขนาด Code (-O0) | ความเร็วในการ Compile เพิ่มขึ้น (-O1) | อัตราส่วนขนาด Code (-O1) |
---|---|---|---|---|
600.perl | 11.39x | 1.27x | 15.00x | 0.97x |
602.gcc | 12.54x | 1.32x | 17.55x | 1.01x |
605.mcf | 9.72x | 1.27x | 12.47x | 0.92x |
620.omnetpp | 21.46x | 1.24x | 26.49x | 1.03x |
623.xalanc | 18.99x | 1.24x | 24.80x | 0.98x |
625.x264 | 10.52x | 1.26x | 15.19x | 0.97x |
631.deepsjeng | 9.60x | 1.25x | 17.56x | 0.97x |
641.jeela | 21.44x | 1.24x | 18.36x | 0.95x |
657.x2 | 10.95x | 1.30x | 15.15x | 0.92x |
ค่าเฉลี่ยเรขาคณิต | 13.34x | 1.27x | 17.58x | 0.97x |
นวัตกรรมทางเทคนิคที่อยู่เบื้องหลังความเร็ว
ความลับของความเร็วของ TPDE-LLVM อยู่ที่แนวทางที่เรียบง่าย แทนที่จะใช้ระบบการเพิ่มประสิทธิภาพแบบหลาย pass ที่ซับซ้อนของ LLVM TPDE ใช้เพียงสาม passes: การทำความสะอาด IR การวิเคราะห์สำหรับ loops และ variable lifetimes และการสร้างโค้ดแบบรวม กระบวนการที่เรียบง่ายนี้จัดการกับ lowering, register allocation และการสร้าง machine code ทั้งหมดในครั้งเดียว
การอภิปรายในชุมชนเผยให้เห็นว่าแนวทางนี้ทำงานได้ดีสำหรับโค้ดที่สร้างโดย compiler ทั่วไปจาก Clang โดยมีการรองรับที่ดีพอสมควรสำหรับ Rust และ Flang เช่นกัน อย่างไรก็ตาม Rust libraries ยอดนิยมบางตัวใช้ vector types พิเศษที่ยังไม่ได้รับการรองรับ ซึ่งจำกัดประโยชน์ในทันทีสำหรับโปรเจกต์ทั้งหมด
ภาพรวมสถาปัตยกรรม TPDE-LLVM
- การออกแบบแบบสามขั้นตอน: การทำความสะอาด/เตรียม IR → การวิเคราะห์ (loop + liveness) → การสร้างโค้ดแบบรวม
- เป้าหมายที่รองรับ: x86-64 และ AArch64 เท่านั้น
- การรองรับ LLVM-IR: ชุดย่อยทั่วไปที่สร้างโดย Clang -O0/-O1
- ตัวเลือกการผสานรวม: ไลบรารี (การคอมไพล์ JIT), เครื่องมือแบบสแตนด์อโลน, การผสานรวม Clang (ต้องใช้แพตช์)
- ข้อจำกัดปัจจุบัน: ไม่รองรับ DWARF, การจัดสรรรีจิสเตอร์แบบพื้นฐาน, แพลตฟอร์ม ELF เท่านั้น
ผลกระทบในโลกจริงและข้อจำกัด
การเพิ่มประสิทธิภาพส่วนใหญ่เป็นประโยชน์ต่อส่วน backend ของการคอมไพล์ ในโปรเจกต์ C/C++ ทั่วไปที่ใช้ Clang frontend (การแยกวิเคราะห์ source code) ใช้เวลา 50-80% ของเวลาการคอมไพล์ ด้วย TPDE-LLVM สัดส่วนนี้เพิ่มขึ้นเป็นกว่า 98% หมายความว่าการเพิ่มความเร็วโดยรวมนั้นน้อยกว่าที่ตัวเลข backend แสดงให้เห็น
การปรับปรุง 10-20 เท่าที่อธิบายไว้ที่นี่ยังไม่ทำงานสำหรับ clang หรือ rustc และเมื่อมันทำงานได้แล้ว มันจะเพิ่มความเร็วเฉพาะส่วน backend เท่านั้น อย่างไรก็ตาม นี่ยังคงเป็นชิงชัยที่น่าทึ่งสำหรับเวลาการคอมไพล์ เพราะอีกสองขั้นตอนสามารถเพิ่มประสิทธิภาพได้อย่างอิสระ
ความท้าทายทางเทคนิคหลายประการยังคงอยู่ ระบบยังไม่รองรับข้อมูลการ debug แบบ DWARF ใช้ register allocation พื้นฐานที่ spill ทุกอย่าง และขาดการรองรับสำหรับแพลตฟอร์มที่ไม่ใช่ ELF นักพัฒนาได้ระบุคุณสมบัติ LLVM-IR เฉพาะที่ทำให้การคอมไพล์ช้าลง รวมถึง constant expressions ภายในฟังก์ชันและโครงสร้างข้อมูลที่มีขนาดไม่จำกัด
ปัญหาคอขวดด้านประสิทธิภาพที่ระบุใน LLVM-IR
ผลกระทบต่อประสิทธิภาพ:
- ConstantExpr ภายในฟังก์ชัน (ต้องการการเขียนใหม่เป็นคำสั่ง)
- ค่า struct/array ที่มีขนาดไม่จำกัด (ความซับซ้อนของรันไทม์แบบกำลังสอง)
- PHINode::getIncomingValForBlock ทำให้เกิดเวลาคอมไพล์แบบกำลังสองสำหรับบล็อกที่มี predecessors มากกว่า 1k
ความท้าทายในการพัฒนา:
- การเข้าถึงโดยตรงไปยัง thread-local globals (ต้องการการสร้างฟังก์ชันเรียก)
- การคำนวณแบบ bit-width ที่ไม่จำกัด (i268 และความกว้างที่ไม่เป็นมาตรฐานคล้ายกัน)
- ปัญหาประสิทธิภาพของ Live::successors ที่ต้องการการแคช
มองไปข้างหน้า
โปรเจกต์นี้แสดงถึงก้าวสำคัญไปสู่การคอมไพล์ที่เร็วขึ้น โดยเฉพาะอย่างยิ่งมีค่าสำหรับ development workflows ที่การทำซ้ำอย่างรวดเร็วมีความสำคัญมากกว่าประสิทธิภาพ runtime ที่เหมาะสมที่สุด แม้ว่ามันจะไม่ทดแทน LLVM optimizing backends สำหรับ release builds TPDE-LLVM อาจกลายเป็นเครื่องมือสำคัญสำหรับนักพัฒนาที่ใช้เวลามากในการรอให้ debug builds คอมไพล์เสร็จ
การเปิดตัวแบบ open-source ช่วยให้ชุมชนที่กว้างขึ้นได้ทดลองกับเทคนิคเหล่านี้และอาจรวมเข้ากับ toolchains ที่มีอยู่ ไม่ว่าสิ่งนี้จะนำไปสู่การยอมรับอย่างแพร่หลายหรือมีอิทธิพลต่อการปรับปรุงใน LLVM เองยังคงต้องติดตาม แต่มันแน่นอนว่าแสดงให้เห็นว่าการปรับปรุงความเร็วการคอมไพล์อย่างมากเป็นไปได้ด้วยการเลือกสถาปัตยกรรมที่เหมาะสม