นักพัฒนา SumatraPDF ละทิ้ง std::function หันมาใช้โซลูชันคอลแบ็กแบบกำหนดเองขนาด 16 ไบต์

ทีมบรรณาธิการ BigGo
นักพัฒนา SumatraPDF ละทิ้ง std::function หันมาใช้โซลูชันคอลแบ็กแบบกำหนดเองขนาด 16 ไบต์

นักพัฒนาเบื้องหลัง SumatraPDF ซึ่งเป็นโปรแกรมดู PDF ยอดนิยมบน Windows ได้แบ่งปันประสบการณ์การเปลี่ยนจากการใช้ฟังก์ชันมาตรฐานของ C++ มาสู่การใช้งานคอลแบ็กแบบกำหนดเอง หลังจากทำงานกับโค้ดเบสมา 16 ปี พวกเขาได้เปลี่ยนการใช้งาน std::function ส่วนใหญ่มาเป็นโซลูชันของตัวเองที่ให้ความสำคัญกับความเรียบง่ายและความสามารถในการดีบักมากกว่าฟีเจอร์ขั้นสูง

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

ประสิทธิภาพและประโยชน์ด้านหน่วยความจำขับเคลื่อนการใช้งานแบบกำหนดเอง

โซลูชันแบบกำหนดเองให้ผลลัพธ์ที่จับต้องได้ในด้านการใช้หน่วยความจำและความเร็วในการคอมไพล์ ในขณะที่ std::function ใช้หน่วยความจำ 64 ไบต์บนระบบ MSVC 64-bit โครงสร้าง Func0 และ Func1 ใหม่ใช้เพียง 16 ไบต์เท่านั้น การลดลงของหน่วยความจำถึงสี่เท่านี้มีความสำคัญในแอปพลิเคชันที่มีคอลแบ็กจำนวนมาก

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

อย่างไรก็ตาม ชุมชนได้ยกประเด็นกังวลที่สมเหตุสมผลเกี่ยวกับการแลกเปลี่ยนประสิทธิภาพบางอย่าง โซลูชันแบบกำหนดเองต้องการการจัดสรรฮีปสำหรับข้อมูลผู้ใช้ ในขณะที่ std::function สามารถเพิ่มประสิทธิภาพคลอเชอร์ขนาดเล็กโดยการเก็บไว้ภายในได้ นี่หมายความว่าข้อได้เปรียบด้านหน่วยความจำไม่ได้ชัดเจนเสมอไป และความใกล้ชิดของแคชอาจได้รับผลกระทบในบางสถานการณ์

การเปรียบเทียบการใช้หน่วยความจำ:

  • std::function: 64 ไบต์ ( MSVC 64-bit )
  • Custom Func0/Func1 แบบกำหนดเอง: 16 ไบต์แต่ละตัว
  • การลดหน่วยความจำ: ลดขนาดลง 75%

ชุมชนถกเถียงการแลกเปลี่ยนระหว่างความเรียบง่ายและมาตรฐาน

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

ผู้สนับสนุนชื่นชมแนวทางที่เป็นจริง โดยเฉพาะอย่างยิ่งเมื่อพิจารณาชื่อเสียงของ SumatraPDF ในด้านความเบาและความเร็ว ขนาด 10 MB ของแอปพลิเคชันตัดกับการบวมของซอฟต์แวร์สมัยใหม่ทั่วไป และการเพิ่มประสิทธิภาพทุกอย่างมีส่วนช่วยในความสำเร็จนี้

โค้ดของฉัน กฎของฉัน ความสุขของฉัน แต่ในเชิงปรัชญา หากคุณเคยสงสัยว่าทำไมซอฟต์แวร์ส่วนใหญ่ในปัจจุบันไม่สามารถเริ่มต้นได้ทันทีและส่งมอบสิ่งของ 100 MB เพื่อแสดงหน้าต่าง นั่นเป็นเพราะโปรแกรมเมอร์ส่วนใหญ่ไม่ได้ใส่ใจหรือพยายามรักษาสิ่งต่างๆ ให้เล็กและเร็ว

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

การแลกเปลี่ยนเปรียบเทียบกับ std::function :

  • ข้อดี: ใช้หน่วยความจำน้อยกว่า, คอมไพล์เร็วกว่า, อ่านรายงานข้อผิดพลาดได้ง่ายกว่า, การใช้งานที่เรียบง่ายกว่า
  • ข้อเสีย: จำกัดเฉพาะลายเซ็นฟังก์ชันที่กำหนด, ต้องจัดการหน่วยความจำด้วยตนเอง, ขาดการปรับปรุงฟังก์ชันขนาดเล็ก, ต้องใช้โค้ดเสริมมากกว่า

บทสรุป

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

อ้างอิง: Simplest C++ callback, from SumatraPDF