12 คะแนน โดย darjeeling 2025-05-23 | 3 ความคิดเห็น | แชร์ทาง WhatsApp

ช่วงนี้กำลังศึกษา free-threading Python เลยเริ่มสนใจ PyO3 แม้จะเป็นบทความเมื่อ 2 ปีก่อน แต่ก็ขอนำมาแชร์ครับ

Making Python 100× Faster with <100 Lines of Rust – สรุป

ภูมิหลัง

  • ไลบรารี Python แกนหลักของไปป์ไลน์ประมวลผล 3-D ภายในบริษัทกลายเป็นคอขวดจากจำนวนผู้ใช้พร้อมกันที่เพิ่มขึ้น
  • การเขียนใหม่ทั้งหมดด้วย Rust มีความเสี่ยงและใช้เวลามาก จึงเลือกแนวทาง เพิ่มประสิทธิภาพเฉพาะบางส่วน

วิธีการ

  1. เริ่มจากการวัดผล: ใช้ sampling profiler py-spy เพื่อระบุคอขวด
  2. นำ Rust เข้ามาแบบค่อยเป็นค่อยไป
    • ใช้ PyO3 + maturin เพื่อเชื่อม Python ↔ Rust
    • ย้ายเฉพาะฟังก์ชัน find_close_polygons ไปเป็น Rust ก่อน
    • จากนั้นย้ายโครงสร้างข้อมูล Polygon ไปเป็น Rust แล้วให้ subclass จากฝั่ง Python
  3. โปรไฟล์-ปรับปรุงซ้ำอย่างต่อเนื่อง
    • ลดการแปลง NumPy → Rust ที่ไม่จำเป็น
    • ลดการจัดสรรหน่วยความจำและการคัดลอก พร้อมทำ micro-optimization ด้วยการคำนวณระยะทางโดยตรง

การเปลี่ยนแปลงด้านประสิทธิภาพ

ขั้น เวลาเฉลี่ยในการรัน (ms) อัตราการปรับปรุง
Python ล้วนเริ่มต้น 293.41
v1 – เฉพาะฟังก์ชันเป็น Rust (--release) 23.44 12.5×
v2 – Polygon ก็เป็น Rust ด้วย 6.29 46.5×
v3 – ตัดการจัดสรรออก·คำนวณโดยตรง 2.90 101×

เทคโนโลยีหลัก

  • PyO3 : FFI ระหว่าง Python ↔ Rust ที่ปลอดภัย
  • maturin : ทำ build และ deploy อัตโนมัติ
  • ndarray / numpy crate : อาร์เรย์และพีชคณิตเชิงเส้นฝั่ง Rust
  • py-spy : profiler ที่มองเห็นได้ถึง native stack

บทเรียน

  • ถ้าเริ่มจากการทำ profiling ก่อน ก็สามารถได้ผลลัพธ์มหาศาลจากการเปลี่ยนโค้ดเพียงเล็กน้อย
  • แม้จะคง Python API เดิมไว้ แค่สลับเฉพาะโมดูล Rust ก็สามารถนำไปใช้กับบริการจริงได้ทันที
  • Rust มีประสิทธิภาพมากพอแม้นำมาใช้แบบบาง ๆ เฉพาะใน “ส่วนที่ต้องการความเร็ว”

3 ความคิดเห็น

 
allinux 2025-05-23

การสร้างส่วนขยาย Python ด้วย c/c++ ทำให้ประสิทธิภาพในการพัฒนาตกลงมากเกินไป แต่ pyo3 สะดวกมากเพราะอย่างน้อยก็มี maturin, cargo ให้ใช้
และโมดูล Python ก็จำเป็นต้องรองรับการคอมไพล์ข้ามแพลตฟอร์มด้วย ซึ่ง rust ก็ทำครอสคอมไพล์ได้สะดวกเช่นกัน.

 
lamanus 2025-05-23

maturin... ทรมาน...

 
aer0700 2025-05-23

พยายามไปให้สุดด้วยการเวกเตอร์ไรซ์ด้วย NumPy ก่อน ถ้าไม่ได้ก็ค่อยเสียบ GPU แล้วเปลี่ยนไปใช้ cupy หรือ torch และถ้ายังไม่ได้อีกก็ค่อยเขียนเนทีฟด้วย cython อะไรแบบนั้น... แต่ดูเหมือนว่าถ้าเลี่ยงการเขียนเนทีฟได้ก็ควรเลี่ยงนะครับ มันเหนื่อยมาก