TPDE-LLVM ให้ความเร็วในการคอมไพล์เร็วกว่า Standard LLVM Backend ถึง 10-20 เท่า

ทีมชุมชน BigGo
TPDE-LLVM ให้ความเร็วในการคอมไพล์เร็วกว่า Standard LLVM Backend ถึง 10-20 เท่า

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 เองยังคงต้องติดตาม แต่มันแน่นอนว่าแสดงให้เห็นว่าการปรับปรุงความเร็วการคอมไพล์อย่างมากเป็นไปได้ด้วยการเลือกสถาปัตยกรรมที่เหมาะสม

อ้างอิง: TPDE-LLVM: 10-20x Faster LLVM -00 Back-End