เจอปัญหา 140 จุดในฟังก์ชันเดียวหลังพอร์ตโค้ด Python สู่ Rust
นักพัฒนาแชร์ประสบการณ์พอร์ตไลบรารีวัดความคล้ายคลึงของข้อความจาก Python สู่ Rust และพบความท้าทายเรื่องความต่างของผลลัพธ์ถึง 140 จุด

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- การพอร์ตโค้ดไม่ใช่แค่ทำให้คอมไพล์ผ่าน แต่ต้องพิสูจน์ว่าโค้ดใหม่ทำงานได้ผลลัพธ์ตรงกันทุกประการ
- การใช้ Subprocess CLI สื่อสารผ่าน JSON ทำให้เกิดข้อจำกัดในการส่งฟังก์ชันแบบ Lambda ไปยัง Rust
- การทำ Differential Fuzzing ช่วยตรวจพบความแตกต่างถึง 140 จุดที่ชุดทดสอบปกติมองไม่เห็น
- ข้อผิดพลาดใน Jaro-Winkler มาจากการละเลยเกณฑ์มาตรฐานความต่าง 0.7 ตามบทความวิชาการอายุ 35 ปี
การพอร์ตไลบรารีจากภาษาหนึ่งไปอีกภาษาหนึ่งไม่ใช่แค่เรื่องของการแปลโค้ดให้ออกมาคอมไพล์ผ่าน แต่ปัญหาทางวิศวกรรมที่ยากกว่าคือการพิสูจน์ว่าโค้ดชุดใหม่ให้ความหมายเดิมเหมือนกับโค้ดต้นฉบับผ่านอัลกอริทึมมากกว่า 30 รูปแบบ กรณีศึกษาครั้งนี้เกิดขึ้นจากการพอร์ตไลบรารีวัดความคล้ายคลึงของสตริงจาก Python ไปเป็นภาษา Rust ในชื่อโปรเจกต์ textdistance-rs
ไลบรารีดั้งเดิมครอบคลุมอัลกอริทึมจำนวนมาก ได้แก่ Edit-based distances (เช่น Levenshtein, Hamming), Token-based measures (เช่น Jaccard, Cosine), Sequence-based methods, Phonetic algorithms และ Compression-based distances รวมกว่า 30 อัลกอริทึมที่ต้องเขียนใหม่ด้วยภาษา Rust ทั้งหมด ก่อนจะเริ่มเขียนโค้ด การตัดสินใจครั้งแรกคือการเชื่อมชุดทดสอบ Python เดิมที่มีอยู่ 397 ชุดเข้ากับโค้ด Rust อย่างไร
- เลือกใช้ Subprocess CLI เป็น Binary อิสระสื่อสารผ่าน JSON
- ใช้ Python Adapter เรียกใช้งานผ่าน Standard Input/Output
- เลือกวิธีนี้เพื่อความรวดเร็ว แต่ปิดกั้นทางเลือกอื่นในอนาคต

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
การตัดสินใจใช้ Subprocess และ JSON แลกมาด้วยความเร็วในการพัฒนาและการแยกกระบวนการทำงานที่สะอาด แต่ก็สร้างข้อจำกัดทางสถาปัตยกรรมอย่างหลีกเลี่ยงไม่ได้ โดยเฉพาะการส่งฟังก์ชันจำพวก Callback หรือ Lambda ที่ไม่สามารถแปลงเป็น JSON ได้ ส่งผลให้ชุดทดสอบบางส่วนไม่สามารถทำงานข้ามระบบได้ตามที่ออกแบบไว้แต่แรก
ช่วงเวลาที่ดีบั๊กยากที่สุดเกิดขึ้นกับอัลกอริทึม Jaro-Winkler ซึ่งอ้างอิงจากบทความวิชาการของ William Winkler ในปี 1990 โดยมีการเพิ่มโบนัสสำหรับสตริงที่มีคำนำหน้าเหมือนกัน แต่เวอร์ชันแรกที่เขียนขึ้นกลับนำโบนัสไปบวกแบบไม่มีเงื่อนไข ซึ่งขัดกับหลักการที่ระบุว่าระบบจะเพิ่มโบนัสต่อเมื่อคะแนน Jaro พื้นฐานเกินเกณฑ์ 0.7 เท่านั้น ไม่เช่นนั้นคะแนนของสตริงที่ไม่เกี่ยวข้องกันจะถูกดันให้สูงขึ้นเกินจริง
"When you port a library, you inherit its citations, not just its code."
ผู้พัฒนาโปรเจกต์ textdistance-rs
คำถามคือเหตุใดจึงพบความแตกต่างมากถึง 140 จุด ทั้งที่ชุดทดสอบยูนิตเทสต์ของ Jaro-Winkler ผ่านทั้งหมด คำตอบคือการใช้เทคนิค Differential Fuzzing ด้วยการรันอินพุตแบบสุ่มทั้งสองระบบแล้วนำผลลัพธ์มาเทียบกัน โดยใช้ไลบรารี Hypothesis สร้างชุดข้อมูลอินพุตอันหลากหลาย ซึ่งช่วยบีบอัดข้อผิดพลาดให้เหลือเคสขนาดเล็กที่นำมาวิเคราะห์บนกระดาษได้ง่าย จนกระทั่งลดความแตกต่างลงเหลือศูนย์ในส่วนที่ผ่านท่อสื่อสารได้
อย่างไรก็ตาม ยังมีอัลกอริทึมประเภท Alignment เช่น Needleman-Wunsch และ Smith-Waterman ที่รับพารามิเตอร์ sim_func เป็น Python Callable สำหรับให้คะแนนความคล้ายคลึงของตัวอักษร ซึ่งฟังก์ชันประเภท Lambda เหล่านี้ไม่สามารถแปลงเป็น JSON เพื่อส่งไปยังกระบวนการทำงานของ Rust ได้ ส่งผลให้ชุดทดสอบจำนวน 22 ชุดไม่สามารถรันได้เนื่องจากข้อจำกัดทางสถาปัตยกรรมตั้งแต่วันแรก
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น