วิธีเพิ่มความเร็วให้โปรแกรมเก่า ด้วยการค่อย ๆ แทรก Rust เข้าไปแทนที่จะเขียนใหม่ทั้งระบบ

อ่าน 7 นาที 0 ผู้อ่าน

วิธีเพิ่มความเร็วให้โปรแกรมเก่า ด้วยการค่อย ๆ แทรก Rust เข้าไปแทนที่จะเขียนใหม่ทั้งระบบ

ทำไมการเขียนโปรแกรมใหม่ทั้งระบบถึงเป็นความเสี่ยง

หลายคนมักมีไอเดียว่าถ้าโปรแกรมปัจจุบันช้า ให้ลองรื้อเขียนใหม่ด้วยภาษาที่เร็วกว่าอย่าง Rust การทำแบบนี้ฟังดูดีในเชิงทฤษฎี แต่ในทางปฏิบัติมักกลายเป็นฝันร้าย เพราะระบบเก่ามักมีเงื่อนไขซับซ้อนที่สะสมมานานหลายปี

Lily Mara วิศวกรจาก Discord ย้ำว่าการรื้อเขียนใหม่ (Full rewrite) มักจะเจอปัญหาทำไม่เสร็จตามกำหนด และที่แย่กว่าคือมักจะทำบั๊ก (ข้อผิดพลาดของโปรแกรม) เดิมกลับมาโดยไม่ตั้งใจ เพราะเราลืมเงื่อนไขเฉพาะตัวที่เคยแก้ไปแล้วในอดีต

แทนที่จะเริ่มจากศูนย์ เราควรหันมามองการปรับแก้เฉพาะจุดที่ช้าจริงๆ โดยไม่ต้องเปลี่ยนโครงสร้างหลักของโปรแกรมทั้งหมด วิธีนี้จะช่วยลดความเสี่ยงและช่วยให้เราเห็นผลลัพธ์ของความเร็วที่เพิ่มขึ้นได้ทันทีโดยไม่ต้องรอกระบวนการพัฒนาที่ยาวนาน

FFI คือทางลัดที่ช่วยให้ภาษาต่างกันคุยกันได้

FFI (Foreign Function Interface) คือกลไกที่ช่วยให้ภาษาโปรแกรมหนึ่งเรียกใช้งานโค้ดของอีกภาษาหนึ่งได้ นี่ไม่ใช่เรื่องใหม่ เพราะระบบปฏิบัติการส่วนใหญ่ถูกสร้างมาให้เรียกใช้โค้ดภาษา C ได้เป็นปกติอยู่แล้ว

เราสามารถใช้เทคนิคนี้เชื่อม Python เข้ากับ Rust เพื่อให้ส่วนที่ต้องการความเร็วสูงทำงานผ่าน Rust ส่วนที่เหลือยังคงรันด้วย Python เหมือนเดิม วิธีนี้คล้ายกับที่ NumPy หรือ TensorFlow ทำ ซึ่งใช้โค้ดภาษาที่เร็วกว่าทำงานเบื้องหลัง

การเลือกจุดที่จะเปลี่ยนเป็น Rust ต้องดูที่ความคุ้มค่า เช่น ฟังก์ชันที่ถูกเรียกใช้บ่อยมากๆ ในทุกๆ การร้องขอ (Request) แม้ฟังก์ชันนั้นจะดูไม่ซับซ้อน แต่ถ้าเปลี่ยนให้เร็วขึ้นได้ 10 เท่า ก็จะลดภาระของเซิร์ฟเวอร์ลงได้มหาศาล

การเตรียมตัวและข้อควรระวัง

การนำ Rust เข้ามาในระบบที่ไม่ได้ใช้ Rust มาก่อน จะทำให้ขั้นตอนการติดตั้งและดูแลระบบ (Deployment) ซับซ้อนขึ้น คุณต้องจัดการเรื่องคอมไพเลอร์ (ตัวแปลงโค้ดให้อยู่ในรูปแบบที่คอมพิวเตอร์เข้าใจ) และไฟล์ไบนารี (ไฟล์โปรแกรมที่พร้อมใช้งาน) ให้ตรงกับเครื่องของทีมพัฒนาทุกคน

คุณต้องมั่นใจว่าทีมงานพร้อมรับมือกับความซับซ้อนที่เพิ่มขึ้น การตัดสินใจเปลี่ยนภาษาในบางส่วนของระบบอาจต้องแลกกับการที่นักพัฒนาต้องเรียนรู้เครื่องมือใหม่ๆ เพิ่มเติม ซึ่งเป็นต้นทุนที่ต้องนำมาพิจารณาควบคู่ไปกับความเร็วที่ได้มา

ก่อนจะเริ่ม ให้ประเมินก่อนว่าระบบของคุณอยู่ในกลุ่มที่รองรับได้ง่ายหรือไม่ เช่น Python, Ruby หรือ Node.js ซึ่งมีเครื่องมือช่วยทำ FFI ที่แข็งแกร่งมาก หากใช้ภาษาเหล่านี้ การเชื่อมต่อกับ Rust จะทำได้ราบรื่นกว่าภาษาอื่นๆ ที่มีข้อจำกัดเรื่องหน่วยความจำหรือตัวจัดการงานภายใน

ตัวอย่างการนำ Rust มาแทนที่ฟังก์ชันใน Python

สมมติว่าคุณมีฟังก์ชันคำนวณทางสถิติใน Python ที่ทำงานช้า เราสามารถย้ายตรรกะนี้ไปเขียนด้วย Rust แล้วเรียกใช้ผ่าน PyO3 (เครื่องมือเชื่อม Python กับ Rust) ได้ดังนี้

// โค้ด Rust ในไฟล์ lib.rs
use pyo3::prelude::*;

#[pyfunction]
fn calculate_sum(numbers: Vec<i32>) -> PyResult<i32> {
    Ok(numbers.iter().sum())
}

#[pymodule]
fn rstats(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(calculate_sum, m)?)
    Ok(())
}

อธิบายโค้ด: บรรทัดที่ 4 คือการประกาศฟังก์ชันคำนวณผลรวม บรรทัดที่ 5 คือการรวมค่าตัวเลขทั้งหมดในรายการ บรรทัดที่ 7-11 คือการกำหนดให้ฟังก์ชันนี้เรียกใช้งานได้จาก Python ผ่านโมดูลชื่อ rstats

ผลลัพธ์: หลังจากคอมไพล์แล้ว คุณจะได้ไฟล์ที่ Python สามารถ import เข้ามาใช้เหมือนเป็นไลบรารีปกติ ทำให้เรียกใช้ฟังก์ชัน calculate_sum ได้เร็วขึ้นกว่าการเขียนคำนวณด้วย Python แบบเดิมหลายเท่าตัว

การเลือกโปรเจกต์ที่เหมาะสม

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

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

การวิเคราะห์ว่าระบบใช้เวลาไปกับการทำงานส่วนไหนมากที่สุดจะช่วยให้คุณตัดสินใจได้แม่นยำขึ้น หากพบว่า 10% ของเวลาเซิร์ฟเวอร์หมดไปกับการทำงานในฟังก์ชันเดียว การเปลี่ยนส่วนนั้นเป็น Rust จะให้ผลตอบแทนที่คุ้มค่ากับเวลาที่ลงทุนไปอย่างแน่นอน

ความปลอดภัยของหน่วยความจำ

เหตุผลหนึ่งที่หลายคนหันมาใช้ Rust คือความปลอดภัยของหน่วยความจำ (Memory Safety - การป้องกันไม่ให้โปรแกรมเข้าถึงพื้นที่หน่วยความจำผิดพลาดจนเกิดบั๊ก) ซึ่งภาษาอย่าง C หรือ C++ มักมีปัญหาในส่วนนี้

เมื่อคุณใช้ Rust ในส่วนที่สำคัญของระบบ คุณจะได้รับประโยชน์จากการตรวจสอบของภาษา Rust ที่เข้มงวด ทำให้โอกาสเกิดช่องโหว่ด้านความปลอดภัยลดลงอย่างมาก ซึ่งเป็นสิ่งที่ระบบขนาดใหญ่ให้ความสำคัญเป็นอันดับต้นๆ

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

สรุปสั้น ๆ

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


ที่มา: Presentation: Accelerating Performance by Incrementally Integrating Rust Into Existing Codebase — InfoQ

แชร์ข่าวนี้

เรียบเรียงจากข่าวของ InfoQ อ่านข่าวต้นฉบับ ↗

← กลับไปหน้าข่าวสาร