บางครั้ง Benchmark ไม่ได้วัดแค่ความเร็ว แต่กำลังเผยให้เราเห็นถึงปรัชญาของภาษานั้นๆ
📅 วันที่เผยแพร่: 2026-02-18
หากเรามองเผิน ๆ การทดสอบนี้ดูเหมือนเป็นเพียงการเปรียบเทียบความเร็วในการประมวลผลระหว่างหลายภาษา อาทิ C, C++, Go และ Rust โดยให้แต่ละภาษาทำงานเดียวกัน คือคำนวณผลรวมของกำลังสองของจำนวนเต็มตั้งแต่ 1 ถึง 100 ล้าน โดยแบ่งงานออกเป็น 4 เธรด โค้ดของทุกภาษามีโครงสร้างแทบจะเหมือนกันทั้งหมด ทั้งการแบ่งช่วงตัวเลข การวนลูปคำนวณ และการนำผลลัพธ์จากแต่ละเธรดมารวมกัน ซึ่งผลลัพธ์ที่ถูกต้องคือ “672,921,401,752,298,880” Rust ดูเหมือนจะสอบตก คำนวณก็ช้า…. แถมได้ผลลัพธ์ที่ออกมาก็ผิดอีก
ปัญหาซ่อนเร้นเมื่อตัวเลขเกินขีดจำกัด
แต่เมื่อพิจารณาให้ลึกลงไป การทดสอบนี้กลับเผยให้เห็นสิ่งที่สำคัญกว่าความเร็ว นั่นคือแนวคิดพื้นฐานของแต่ละภาษาในการจัดการกับ ความถูกต้องของตัวเลขและความรับผิดชอบของผู้พัฒนา
ปัญหาที่ซ่อนอยู่ในโจทย์นี้คือ ขนาดของตัวเลขที่ใช้ในการคำนวณ แม้ว่าค่าแต่ละพจน์ ( i^2 ) จะยังอยู่ในขอบเขตของจำนวนเต็ม 64 บิต แต่เมื่อเริ่มนำค่าจำนวนมากมาสะสมรวมกัน ผลรวมสุดท้ายได้ข้ามขีดจำกัดนั้นไปแล้วโดยไม่รู้ตัว จุดนี้เองที่ทำให้ภาษาต่าง ๆ แสดง “บุคลิก” ของตนออกมาอย่างชัดเจน ทั้งที่โจทย์และโค้ดดูเหมือนจะเหมือนกันทุกประการ
แนวทางของ C, C++ และ Go
ในฝั่งของ C และ C++ การใช้ long long ทำให้เกิด signed integer overflow ซึ่งตามมาตรฐานของภาษาแล้วถือเป็น พฤติกรรมที่ไม่ถูกกำหนดให้แน่ชัด (undefined behavior) นั่นหมายความว่า compiler สามารถเลือกทำอะไรก็ได้กับโค้ดชุดนี้ ผลลัพธ์ที่ได้ออกมาดูเหมือนจะถูกต้องก็จริง แต่ความถูกต้องนั้นเกิดจากความบังเอิญ? ไม่ใช่สิ่งที่ภาษาให้การรับรอง การไม่มีคำเตือนหรือ error จึงไม่ได้แปลว่าโค้ดนั้นปลอดภัยหรือถูกต้องตามหลักคณิตศาสตร์
Go เลือกแนวทางที่ต่างออกไป ด้วยการใช้ uint64 ซึ่งกำหนดพฤติกรรมของ overflow ไว้อย่างชัดเจนให้เป็นการวนรอบ (wrap-around) ตามโมดูลัส (2^64) โปรแกรมจึงรันจนจบและให้ผลลัพธ์ที่ดูเสถียรโดยไม่มีการแจ้งเตือนใด ๆ อย่างไรก็ตาม ความถูกต้องของผลลัพธ์ในกรณีนี้ขึ้นอยู่กับความเข้าใจของผู้พัฒนาล้วน ๆ ภาษาไม่ได้บังคับให้หยุดคิดว่าตัวเลขที่ได้ยังคงมีความหมายทางคณิตศาสตร์หรือไม่
เส้นทางของ Rust ที่ไม่ยอมให้ผ่านไปเงียบๆ
Rust กลับเลือกเดินคนละเส้นทางอย่างชัดเจน ภาษาไม่ยอมให้การ overflow ผ่านไปแบบเงียบ ๆ โดยไม่ตั้งคำถาม เมื่อใช้ชนิดข้อมูล 64 บิต การคำนวณนี้จะถูกจับได้ทันที ในโหมด debug โปรแกรมจะหยุดทำงาน ส่วนในโหมด release จะเกิดการ wrap แต่ก็เป็นผลจากการตัดสินใจของผู้พัฒนาเอง หากต้องการผลลัพธ์ที่ถูกต้องจริง ผู้พัฒนาจำเป็นต้องระบุชนิดข้อมูลที่ใหญ่พอ เช่น u128 อย่างชัดเจน
จุดนี้สะท้อนว่า Rust ปฏิเสธที่จะ “เดาแทนผู้พัฒนา” ว่าการ overflow นั้นยอมรับได้หรือไม่ เมื่อเลือกชนิดข้อมูลได้ถูกต้องแล้ว Rust จะให้ผลลัพธ์ทางคณิตศาสตร์ที่ตรงกับความจริง โดยไม่พึ่งพา undefined behavior หรือการ wrap แบบเงียบ ๆ
ปรัชญาการออกแบบผ่าน Benchmark
การทดสอบจึงไม่ใช่การแข่งขันเรื่องความเร็วอีกต่อไป แต่เป็นภาพสะท้อนของปรัชญาการออกแบบภาษาอย่างชัดเจน C และ C++ ให้อิสระสูงสุดแก่ผู้พัฒนาโดยแลกกับการรับประกันความถูกต้อง Go เน้นความเรียบง่ายและความคาดเดาได้ แม้จะยอมให้ข้อผิดพลาดเชิงตัวเลขผ่านไปได้ ส่วน Rust เลือกให้ความสำคัญกับความถูกต้องและความชัดเจนของเจตนาผู้พัฒนาเป็นอันดับแรก แม้จะทำให้โค้ดดูเข้มงวดขึ้นในตอนแรกก็ตาม
มาถึงตรงนี้ benchmark นี้ไม่ได้แสดงให้เห็นว่า Rust ด้อยประสิทธิภาพกว่าใคร แต่แสดงให้เห็นว่า Rust เลือกไม่แลกความถูกต้องกับความสะดวก และเมื่อระบบถูกกดดันด้วยขนาดของข้อมูล ความแตกต่างเชิงปรัชญานี้เองที่ปรากฏออกมาอย่างเด่นชัด แต่ละภาษาก็มีความงดงามของตนเอง ว่าแต่คุณทำไมถึงเลือกเขียน Rust ลองตอบคำถามนี้ดูครับ
Credit & Reference: