13 คะแนน โดย GN⁺ 2025-09-30 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • บทความนี้อธิบายแนวคิดและการออกแบบในการแปลง โค้ด Python ล้วน ให้เป็น ไฟล์รันข้ามแพลตฟอร์ม ด้วยการ คอมไพล์ล่วงหน้า (AOT) โดยยกตัวอย่างเป็นกรณีศึกษา
  • แนวคิดหลักคือไม่สร้าง JIT ใหม่หรือเขียนใหม่ด้วย C++ แต่ใช้ไปป์ไลน์ symbolic tracing → IR → สร้างโค้ด C++ → คอมไพล์หลายเป้าหมาย เพื่อสร้าง เคอร์เนลที่ปรับแต่งประสิทธิภาพแล้ว
  • ใช้ type annotation ตาม PEP 484 เป็นจุดตั้งต้นของการเผยแพร่ชนิดข้อมูล และใช้ AI code generation สร้าง โอเปอเรเตอร์ C++ หลายร้อยตัวโดยอัตโนมัติ เพื่อรองรับการเรียกใช้ไลบรารีได้กว้างขวาง เช่น Numpy, OpenCV, PyTorch
  • สำหรับฟังก์ชัน Python เดียวกัน ระบบจะ สร้างและแจกจ่ายเส้นทางการทำงานหลายแบบจำนวนมาก แล้วใช้ telemetry จากการวัดจริง เพื่อเลือก เวอร์ชันที่เร็วที่สุด ตามกลยุทธ์ เพิ่มประสิทธิภาพเชิงประจักษ์
  • เป้าหมายคือมอบ ไบนารีพกพาขนาดเล็กและรวดเร็ว ที่ไม่พึ่งพาคอนเทนเนอร์ เพื่อใช้เป็นหน่วยสำหรับการแจกจ่ายที่ รันได้ทุกที่ ตั้งแต่เซิร์ฟเวอร์ เดสก์ท็อป มือถือ ไปจนถึงเว็บ

Foreword

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

Introduction

  • เป้าหมายคือคอมไพล์ Python ที่ไม่ต้องแก้ไขโค้ด แบบ AOT เต็มรูปแบบ ให้ ทำงานได้โดยไม่ต้องมีอินเทอร์พรีเตอร์ มี ความเร็วใกล้เคียง C/C++ และ รันได้บนทุกแพลตฟอร์ม
  • ต่างจากความพยายามก่อนหน้าอย่าง Jython, RustPython, Numba, PyTorch, Mojo ที่เลือกแนวทาง แปลงโค้ดและสร้างเคอร์เนล แทนการ แทนที่ภาษา หรือแทนที่รันไทม์
  • ฟังก์ชัน Python ที่คอมไพล์แล้วเหล่านี้ถูกใช้งานอยู่แล้วบน อุปกรณ์หลายพันเครื่องในทุกเดือน

Containers Are the Wrong Way to Distribute AI

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

Arm64, Apple, and Unity: How It All Began

  • ในช่วงที่ Apple เปลี่ยนผ่านสู่ arm64 มีกรณีศึกษาของ Unity ที่ใช้ IL2CPP แปลง CIL เป็น C++ เพื่อให้ คอมไพล์ไปยังทุกเป้าหมายได้ ซึ่งถูกนำมาใช้เป็นต้นแบบ
  • จากนั้นจึงตั้งวิสัยทัศน์ว่าจะนำแนวคิดเดียวกันนี้มา ประยุกต์กับ Python เพื่อให้ได้เส้นทางโค้ดที่ รันได้ทุกที่

Sketching Out a Python Compiler

  • การออกแบบระดับสูงประกอบด้วยขั้นตอน Python input → symbolic trace (IR) → สร้าง C++ → คอมไพล์หลายเป้าหมาย
  • เหตุผลที่ไม่สร้าง object code โดยตรงจาก IR แต่เลือกใช้ C++ เป็นผลลัพธ์ขั้นกลาง ก็เพื่อใช้ประโยชน์จาก เส้นทางเร่งความเร็ว อย่าง CUDA, MLX, TensorRT, AMX ได้อย่างเต็มที่
  • เป้าหมายคือให้ได้ สถาปัตยกรรมที่ขยายต่อได้ง่าย และสามารถแทรก เส้นทางที่เหมาะที่สุดตามฮาร์ดแวร์ ได้สะดวก

Building a Symbolic Tracer for Python

  • การติดตามแบบเดิมที่อิง PyTorch FX มีข้อจำกัด เพราะ ต้องอาศัยการรันจริง และ รองรับเฉพาะโอเปอเรชันของ PyTorch
  • จึงหันไปสร้าง symbolic tracer ที่อิงการพาร์ส AST เพื่อแปลง control flow และการตีความการเรียกใช้ ให้เป็น IR
  • ปัจจุบัน tracer นี้รองรับความสามารถอย่าง static analysis, partial evaluation และการสังเกต live value ผ่านแซนด์บ็อกซ์

Lowering to C++ via Type Propagation

  • เพื่อเชื่อม Python ที่มี dynamic typing เข้ากับ C++ ที่มี static typing จึงใช้เทคนิค type propagation
  • เมื่อทราบชนิดข้อมูลของอาร์กิวเมนต์ขาเข้า ก็สามารถ อนุมานชนิดของตัวแปรระหว่างทางได้แบบกำหนดแน่นอน ตาม นิยามของโอเปอเรเตอร์
  • จากนั้นจะแมปแต่ละโอเปอเรชันของ Python ไปยัง อิมพลีเมนเทชัน C++ ที่สอดคล้องกัน พร้อม เผยแพร่ชนิดข้อมูลตลอดทั้งฟังก์ชัน

Seeding the Type Propagation Process

  • ระบบใช้ type annotation ตาม PEP 484 เป็นจุดตั้งต้นของการทำ type propagation
  • แม้จะขัดกับหลักการ ไม่แก้ไขโค้ดต้นฉบับ แต่ก็ถูกมองว่าเป็น ทางประนีประนอมที่ยอมรับได้ เพื่อให้ได้ อินเทอร์เฟซที่กระชับและเข้ากันได้ดี
  • นอกจากนี้ยังมีข้อจำกัด เช่น การจำกัดจำนวนชนิดข้อมูลในฟังก์ชันซิกเนเจอร์ เพื่อให้มั่นใจว่า อินเทอร์เฟซสำหรับผู้ใช้ยังคงเรียบง่าย

Building a Library of C++ Operators

  • ไม่ได้จำเป็นต้องอิมพลีเมนต์ทุกฟังก์ชันเป็น C++ โดยตรง แต่ต้องมีการเขียนเองหรือสร้างอัตโนมัติสำหรับ leaf operation ที่ trace ต่อไม่ได้ เท่านั้น
  • โค้ด Python จำนวนมากประกอบขึ้นจาก การผสมกันของโอเปอเรชันพื้นฐานเพียงไม่กี่ชนิด ทำให้ ชุดโอเปอเรเตอร์ที่ต้องครอบคลุม มีขนาดค่อนข้างเล็ก
  • ด้วย การสร้างโค้ดด้วย LLM และโครงสร้างพื้นฐานสำหรับ ข้อจำกัด การทดสอบ และ conditional compilation จึงสามารถทำ อิมพลีเมนเทชันของฟังก์ชันหลายร้อยตัว ใน Numpy, OpenCV, PyTorch และอื่น ๆ ได้โดยอัตโนมัติ

Performance Optimization via Exhaustive Search

  • จากบทเรียนที่ว่า การปรับแต่งประสิทธิภาพเป็นเรื่องเชิงประจักษ์เสมอ ระบบจึงสร้าง เวอร์ชันอิมพลีเมนเทชันทุกแบบที่เป็นไปได้ แล้วเลือกสิ่งที่ดีที่สุดจาก การวัดเปรียบเทียบจริง
  • ตัวอย่างเช่น บน Apple Silicon แม้แต่การ resize เพียงอย่างเดียว ก็ยังสร้างได้หลายเส้นทาง เช่น Accelerate, vImage, Core Image, Metal และทำการแจกจ่าย ไบนารีหลายตัวที่ให้ความสามารถเดียวกัน
  • ระบบเก็บ latency รายเส้นทาง ด้วย telemetry แบบละเอียด แล้วใช้ แบบจำลองทางสถิติ เพื่อทำนายและเลือก เวอร์ชันที่เร็วที่สุด
  • ผลลัพธ์ในมุมผู้ใช้คือประสบการณ์การรันที่ เร็วขึ้นโดยอัตโนมัติเมื่อเวลาผ่านไป

Designing a User Interface for the Compiler

  • เพื่อให้ประสบการณ์ของนักพัฒนา แทบไม่มีเส้นโค้งการเรียนรู้ จึงเลือกใช้ PEP 318 decorator @compile เป็นอินเทอร์เฟซ
  • CLI จะใช้ decorator นี้เป็น entrypoint เพื่อสำรวจ กราฟของโค้ดที่พึ่งพากัน และคอมไพล์มัน
  • อาร์กิวเมนต์ของ decorator รองรับ tag, description, sandbox, metadata เพื่อสนับสนุน การทำซ้ำสภาพแวดล้อมและการระบุแบ็กเอนด์ เช่น ONNXRuntime, TensorRT, CoreML, IREE, QNN

Closing Thoughts

  • ฟีเจอร์อย่าง exception, lambda, recursion, class ยังอยู่ในสถานะ รองรับบางส่วนหรือยังไม่รองรับ โดยเฉพาะในกรณีของ ชนิดข้อมูลเชิงประกอบและชนิดข้อมูลลำดับสูง ที่ยังต้องขยายความสามารถของ type propagation
  • ประสบการณ์การดีบัก ก็ยังเป็นโจทย์สำคัญ เพราะการคอมไพล์แบบเน้นประสิทธิภาพทำให้ข้อมูลสัญลักษณ์ลดลงและ ติดตามปัญหาได้ยากขึ้น
  • std::span, concepts และ coroutines ของ C++20 เป็นฐานสำคัญ ขณะที่ std::generator, <stdfloat>, <stacktrace> ของ C++23 คาดว่าจะช่วยเรื่อง สตรีมมิง, half/bfloat16 และการติดตาม exception
  • เป้าหมายสุดท้ายคือสร้าง ไฟล์รันขนาดเล็ก รวดเร็ว และปลอดภัยโดยไม่ต้องพึ่งคอนเทนเนอร์ เพื่อทำให้ เวิร์กโหลด AI อย่าง embedding หรือ detection กลายเป็นหน่วยการแจกจ่ายที่ รันได้ทุกที่

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

 
secret3056 2025-09-30

นึกว่าเป็นอะไรแบบ APE แต่ก็ไม่ใช่แบบนั้นนะ