ชุมชนโปรแกรมเมอร์กำลังหารือกันอย่างกระตือรือร้นเกี่ยวกับประสิทธิผลในโลกจริงของเทคนิคการปรับปรุง lexer ขั้นสูง หลังจากได้รับแรงบันดาลใจจากบทความล่าสุดเรื่องการสร้าง compiler ที่เร็วเป็นพิเศษ แม้ว่าแนวทางที่ซับซ้อนอย่างการใช้ jump table แบบ hand-rolled และการจัดการหน่วยความจำแบบกำหนดเองจะแสดงให้เห็นความเป็นไปได้ในทางทฤษฎี แต่นักพัฒนากำลังตั้งคำถามว่าการปรับปรุงเหล่านี้จะให้ผลตอบแทนด้านประสิทธิภาพที่มีความหมายในทางปฏิบัติหรือไม่
เทคนิคการปรับปรุงประสิทธิภาพ Lexer หลักที่กล่าวถึง:
- Jump tables แบบเขียนเองเทียบกับการปรับปรุง switch ที่ compiler สร้างขึ้น
- การจัดสรรหน่วยความจำแบบกำหนดเองผ่าน FileLoader interfaces
- การประมวลผล file stream โดยตรงเทียบกับแนวทางที่ใช้ string-view
- การจัดเก็บ token string โดยใช้ character arrays ขนาดคงที่ (แนะนำ 64 bytes)
- Memoization สำหรับการประมวลผลตัวเลข (รายงานการปรับปรุงความเร็ว 64%)
- Perfect hashing สำหรับการระบุ keyword
- ความเข้ากันได้ข้ามภาษา ( C , C++ , Rust , Go )
การปรับปรุง Compiler สมัยใหม่อาจทำให้เทคนิคแบบ Manual ล้าสมัย
ประเด็นหลักที่เป็นที่ถกเถียงคือการใช้งาน jump table แบบ manual จะมีประสิทธิภาพเหนือกว่าการปรับปรุงของ compiler สมัยใหม่จริงหรือไม่ นักพัฒนาบางคนตั้งคำถามว่า compiler ร่วมสมัยสร้าง jump table ที่มีประสิทธิภาพสำหรับ switch statement ขนาดใหญ่อยู่แล้วหรือไม่ ซึ่งอาจทำให้ทางเลือกที่เขียนด้วยมือไม่จำเป็น การถกเถียงนี้เน้นย้ำถึงความตึงเครียดที่ดำเนินต่อไประหว่างเทคนิคการปรับปรุงระดับต่ำแบบดั้งเดิมกับการไว้วางใจความฉลาดของ compiler สมัยใหม่
การหารือเผยให้เห็นประสบการณ์ที่แตกต่างกันในสถาปัตยกรรมไมโครต่างๆ และกรณีการใช้งานเฉพาะ ในขณะที่นักพัฒนาบางคนรายงานการปรับปรุงที่วัดได้จากการปรับปรุงแบบ manual คนอื่นๆ แนะนำว่าประโยชน์อาจไม่สม่ำเสมอขึ้นอยู่กับแพลตฟอร์มเป้าหมายและโครงสร้างโปรแกรม
ข้อพิจารณาด้านประสิทธิภาพ:
- การปรับแต่งคอมไพเลอร์สมัยใหม่อาจสร้าง jump table ที่มีประสิทธิภาพโดยอัตโนมัติ
- การเพิ่มประสิทธิภาพแตกต่างกันอย่างมีนัยสำคัญในสถาปัตยกรรมไมโครต่างๆ
- การทดสอบประสิทธิภาพมีความจำเป็นเนื่องจากผลลัพธ์ที่ไม่สม่ำเสมอในแพลตฟอร์มต่างๆ
- การเปลี่ยนเส้นทางการจัดสรรหน่วยความจำสามารถให้ความเร็วที่เพิ่มขึ้นอย่างมาก
- การเปรียบเทียบคีย์เวิร์ดโดยใช้จำนวนเต็ม 64 บิตสำหรับคีย์เวิร์ดที่มีขนาด ≤8 ไบต์
- การประมวลผลแบบ stream แลกเปลี่ยนประสิทธิภาพเพื่อความยืดหยุน
แนวทางทางเลือกได้รับความนิยมในหมู่ผู้ปฏิบัติงาน
นักพัฒนาที่มีประสบการณ์กำลังแบ่งปันกลยุทธ์ทางเลือกที่ให้ความสำคัญกับความเรียบง่ายและความสามารถในการบำรุงรักษามากกว่าการปรับปรุงที่ซับซ้อน แนวทางหนึ่งเกี่ยวข้องกับการทำงานโดยตรงกับ file stream แทนที่จะเป็น string view ซึ่งอาจเสียสละประสิทธิภาพบางส่วนแต่เปิดใช้งานความสามารถในการประมวลผล stream วิธีการนี้สามารถปรับให้เข้ากับประเภทอินพุตต่างๆ ได้โดยใช้ฟังก์ชันไลบรารีมาตรฐาน
เทคนิคปฏิบัติอีกอย่างหนึ่งเกี่ยวข้องกับการจัดเก็บ token string โดยตรงภายในโครงสร้าง token โดยใช้ character array ขนาดคงที่ แนวทางนี้ลดการตัดสินใจในระหว่างกระบวนการ lexing โดยมุ่งเน้นไปที่การจำแนกอักขระพื้นฐานแทนที่จะเป็นการจัดการสถานะที่ซับซ้อน
ผมพยายามลดการตัดสินใจในระหว่างการ lexing จริงๆ แล้ว ณ จุดที่ใช้งาน เราสนใจเฉพาะสิ่งที่น้อยมากเท่านั้น: lexeme เริ่มต้นด้วยตัวอักษรหรือตัวเลขหรือไม่?; เป็น whitespace หรือไม่ และ whitespace นั้นเป็นบรรทัดใหม่หรือไม่?; หรือดูเหมือน operator หรือไม่?
ผลการ Benchmark แสดงให้เห็นการเพิ่มประสิทธิภาพที่หลากหลาย
ชุมชนแสดงความสนใจเป็นพิเศษในการวัดประสิทธิภาพที่เป็นรูปธรรม โดยเฉพาะอย่างยิ่งเกี่ยวกับเทคนิคการปรับปรุงหน่วยความจำ นักพัฒนาบางคนแสดงความประหลาดใจต่อการเพิ่มความเร็วที่รายงานจาก memoization ในการประมวลผลตัวเลข แม้ว่าจะยังมีคำถามเกี่ยวกับความสม่ำเสมอของผลลัพธ์เหล่านี้ในสถานการณ์ต่างๆ
การเน้นย้ำเรื่อง benchmarking สะท้อนถึงแนวโน้มที่กว้างขึ้นไปสู่การตัดสินใจปรับปรุงที่อิงหลักฐานแทนที่จะพึ่งพาการปรับปรุงประสิทธิภาพเชิงทฤษฎีเพียงอย่างเดียว แนวทางนี้ช่วยให้นักพัฒนาแยกแยะระหว่างการปรับปรุงที่ให้ประโยชน์จริงกับที่เพิ่มความซับซ้อนโดยไม่มีผลตอบแทนที่มีความหมาย
การใช้งานข้ามภาษาขยายขอบเขตการปรับปรุง
แม้จะมุ่งเน้นไปที่การใช้งาน C และ C++ แต่กลยุทธ์การปรับปรุงเหล่านี้หลายอย่างสามารถแปลไปใช้กับภาษาโปรแกรมอื่นๆ ได้อย่างมีประสิทธิภาพ เทคนิคอย่าง jump table, memory mapping, deferred parsing และ string interning ทำงานได้ในภาษาต่างๆ รวมถึง Rust และ Go
ความเข้ากันได้ข้ามภาษานี้ทำให้การหารือเรื่องการปรับปรุงเกี่ยวข้องกับผู้ชมนักพัฒนาที่กว้างขึ้น แม้ว่ารายละเอียดการใช้งานเฉพาะและลักษณะประสิทธิภาพอาจแตกต่างกันระหว่างภาษาและสภาพแวดล้อม runtime ของแต่ละภาษา
อ้างอิง: Strategies for Very Fast Compilers