3 คะแนน โดย GN⁺ 2025-12-26 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • อินเทอร์พรีเตอร์แบบ tail-calling ของ CPython แสดงประสิทธิภาพเร็วกว่าแบบเดิมราว 15% ในสภาพแวดล้อม Windows x86-64
  • บน macOS AArch64 (XCode Clang) ก็ยืนยันได้ว่ามีประสิทธิภาพดีขึ้นราว 5% และบน Windows ใช้ความสามารถเชิงทดลองของ MSVC 2026
  • ในเบนช์มาร์ก pyperformance การทดสอบส่วนใหญ่มีความเร็วเพิ่มขึ้น และบางรายการดีขึ้นสูงสุดถึง 78%
  • สาเหตุหลักของประสิทธิภาพที่ดีขึ้นวิเคราะห์ได้ว่าเกิดจาก การรีเซ็ต optimization heuristic ของคอมไพเลอร์และการปรับปรุง inlining
  • เมื่อ Python 3.15 ออกรุ่นจริง จะมีการเปิดใช้เป็นค่าเริ่มต้นกับบิลด์ที่อิง Visual Studio 2026

การปรับปรุงประสิทธิภาพของอินเทอร์พรีเตอร์แบบ tail-calling

  • วัดได้ว่า อินเทอร์พรีเตอร์แบบ tail-calling ของ CPython เร็วกว่า อินเทอร์พรีเตอร์แบบ switch-case เดิมราว 15% บน Windows x86-64
    • อ้างอิงจาก pyperformance ค่าเฉลี่ยเรขาคณิตดีขึ้น 15~16%
    • บางเบนช์มาร์กเร็วขึ้นสูงสุด 78% ขณะที่มีเพียงไม่กี่กรณีที่ช้าลง 60%
  • บน macOS AArch64 (XCode Clang) ยืนยันได้ว่าประสิทธิภาพดีขึ้นราว 5%
  • ผลลัพธ์นี้ใช้ได้ภายใต้สมมติฐานว่าไม่มีการเปลี่ยนแปลงระหว่างรอบการพัฒนา Python 3.15

เปรียบเทียบโครงสร้างของอินเทอร์พรีเตอร์

  • วิธีการสร้างอินเทอร์พรีเตอร์ด้วย C แบ่งได้เป็น 3 แบบคือ switch-case, computed goto, และ tail-call threaded
    • switch-case: จัดการการแตกแขนงตามคำสั่งแต่ละตัว
    • computed goto: ส่วนขยายของ GCC/Clang ที่กระโดดไปยังที่อยู่ของสาขาโดยตรง
    • tail-call threaded: แยกตัวจัดการไบต์โค้ดแต่ละตัวเป็นฟังก์ชัน แล้วทำ tail call ไปยังฟังก์ชันถัดไป
  • ในอดีต C compiler ไม่ได้รับประกันการทำ tail call optimization จึงมีความเสี่ยงเรื่อง stack overflow
  • ด้วยแอตทริบิวต์ __attribute__((musttail)) ของ Clang และ [[msvc::musttail]] ของ MSVC จึงสามารถบังคับใช้ tail call ได้

ผลการบิลด์ด้วย MSVC 2026 สำหรับ Windows

  • ในการบิลด์ CPython ที่ใช้ความสามารถเชิงทดลองของ MSVC เบนช์มาร์กส่วนใหญ่มีความเร็วเพิ่มขึ้น
    • ตัวอย่างผลลัพธ์:
      • spectralnorm: 1.48 เท่า
      • nbody: 1.35 เท่า
      • bm_django_template: 1.18 เท่า
      • xdsl: 1.14 เท่า
  • มีการสะท้อนข้อมูลนี้อย่างเป็นทางการในเอกสาร “What’s New” ของ Python 3.15
    • สามารถใช้งาน อินเทอร์พรีเตอร์แบบ tail-calling ได้ในบิลด์ Visual Studio 2026 (MSVC 18)
    • ไลบรารี Python ล้วนเร็วขึ้นราว 15% และสคริปต์ขนาดเล็กเร็วขึ้นได้สูงสุด 40%

สาเหตุของประสิทธิภาพที่ดีขึ้น

  • การทำ tail-calling ช่วย รีเซ็ต optimization heuristic ของคอมไพเลอร์ และทำให้เกิดการสร้างโค้ดที่มีประสิทธิภาพมากขึ้น
  • ลูปอินเทอร์พรีเตอร์เดิมของ CPython ประกอบด้วยฟังก์ชันเดียวราว 12,000 บรรทัด ทำให้ การเพิ่มประสิทธิภาพแบบ inlining ล้มเหลวบ่อยครั้ง
    • มีหลายกรณีที่คอมไพเลอร์ปฏิเสธการ inlining เพื่อหลีกเลี่ยงการเพิ่มขนาดโค้ด
  • ในแบบ tail-calling ฟังก์ชันถูกแยกออก ทำให้ ฟังก์ชันเรียบง่ายสามารถถูก inline ได้
    • ตัวอย่างเช่น ฟังก์ชันง่าย ๆ อย่าง PyStackRef_CLOSE_SPECIALIZED ถูก inline
  • มีรายงานปรากฏการณ์เดียวกันนี้แม้ในบิลด์แบบ PGO (profile-guided optimization)

วิธีบิลด์และการใช้งาน

  • ขณะนี้ ยังบิลด์จากซอร์สได้เท่านั้น
    • ในสภาพแวดล้อม Visual Studio 2026 ให้บิลด์ด้วยคำสั่งต่อไปนี้
      $env:PlatformToolset = "v145"
      ./PCbuild/build.bat --tail-call-interp -c Release -p x64 --pgo
      
  • ในอนาคตเมื่อการพัฒนา Python 3.15 มีเสถียรภาพ จะมีการแจกจ่ายไบนารีอย่างเป็นทางการ

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

 
GN⁺ 2025-12-26
ความคิดเห็นจาก Hacker News
  • แชร์โค้ดส่วนสำคัญที่อยากให้รวมอยู่ในบทความบล็อก
    เป็นตัวอย่างที่แสดงความต่างของการกำหนดแอตทริบิวต์ musttail และ preserve_none ระหว่าง MSVC กับ Clang
    แอตทริบิวต์เหล่านี้ต้องติดกับตัวประกาศฟังก์ชัน และจะไม่ทำงานหากวางไว้ในตำแหน่งตัวระบุฟังก์ชัน
    ลิงก์โค้ดที่เกี่ยวข้อง
    น้ำเสียงเหมือนจะสื่อว่า Microsoft บอกเรื่องฟีเจอร์ที่ไม่เปิดเผยสู่สาธารณะแบบนี้เฉพาะกับโปรเจกต์ที่มองว่าสำคัญ
    • ฉันเข้าใจผิดเอง [[msvc::musttail]] จริง ๆ แล้วเป็นแอตทริบิวต์ที่มีเอกสารทางการ
      จะไปแก้บทความบล็อกให้สะท้อนเรื่องนี้
      คอมเมนต์ HN ที่เกี่ยวข้อง
    • มีการตั้งคำถามว่า “ที่บอกเพราะมันสำคัญ หรือบอกเพราะเป็นประโยชน์กับพวกเขาเองกันแน่?”
  • นึกถึงกรณีตอน Python 3.14 ที่เคยมีการรายงานว่าประสิทธิภาพดีขึ้นผิด ๆ เพราะบั๊กของ LLVM 19 และหวังว่าคราวนี้จะไม่เกิดแบบนั้น
    พออ่านแล้วก็เห็นว่าแนวทางนี้ให้ความสำคัญกับความโปร่งใสและฟีดแบ็กที่รวดเร็ว เลยคิดว่าน่าเชื่อถือ
    ถ้ามีการตรวจสอบด้วยคอมไพเลอร์ข้ามชุดหรือมีการตรวจสอบอิสระก็จะยิ่งดี แต่ก็มองว่าน่าเชื่อถือได้เพราะผู้เขียนเปิดเผยอย่างโปร่งใสเต็มที่
    • ผู้เขียนย้อนความว่าความผิดพลาดครั้งนั้นกลับเป็นอุบัติเหตุที่โชคดี
      เพราะเปิดเผยตั้งแต่เนิ่น ๆ Nelson เลยพบบั๊กของ Clang 19 และแก้ได้ก่อนออกรีลีสจริง
      ครั้งนี้มั่นใจกว่าเพราะมีการปรับปรุงสองอย่างคือ dispatch logic และ inlining
      MSVC สามารถแปลง switch-case interpreter ให้เป็น threaded code ได้ในบางเงื่อนไข แต่ CPython ซับซ้อนเกินไปจนใช้การปรับแต่งนั้นไม่ได้
      แทนที่จะเป็นแบบนั้น แนวทาง tail call ทำให้คนเขียนโค้ด C ควบคุมได้มากกว่า
      อ้างอิงที่เกี่ยวข้อง: เงื่อนไข threaded code ของ MSVC, issue ที่เกี่ยวกับ forceinline
    • ข้อดีของดีไซน์ใหม่คือพึ่งพาอารมณ์ของการ optimize จากคอมไพเลอร์น้อยลง
      ก่อนหน้านี้ optimization อย่าง tail duplication อาจเกิดหรือไม่เกิดตามการตัดสินใจของคอมไพเลอร์ แต่ตอนนี้อินเทอร์พรีเตอร์สามารถแสดงรูปแบบ machine code ที่ต้องการได้โดยตรง
      ลิงก์การอภิปรายก่อนหน้า
  • มีคนแชร์ว่าเคยมีงานนำเสนอที่พูดถึงปัญหาคอมไพเลอร์ในอดีต พร้อมแนบ วิดีโองานบรรยาย EuroPython 2025
  • กำลังทำแอป GUI สำหรับ Windows ด้วย Python อีกครั้งหลังจากไม่ได้ทำนาน
    เลือก Python เพราะecosystem ของ VS หนักเกินไปเมื่อเทียบกับ C#/MAUI
    Tkinter ใช้งานไม่ถนัด ส่วน Qt ก็มีภาระในการเรียนรู้สูง เลยใช้คู่ wxGlade + wxPython
    ต้องการแค่ dependency เดียวที่ติดตั้งผ่าน pip ได้ และชอบความรู้สึกแบบ Pythonicในการใช้งาน
    เลยยินดีกับการปรับปรุง runtime บน Windows
    • ฉันชอบชุด Python + Qt/PySide มากกว่า
      ทำ UI ได้เร็วด้วย QtCreator แล้วค่อยต่อ logic ด้วย Python ทำให้พัฒนาได้เร็วมาก
    • มีคนแนะนำ pyfltk ด้วย บน GNU/Linux ถือว่าใช้ได้ดีทีเดียว
    • ถ้า GUI สำคัญ LINQPad ก็น่าพิจารณา เป็นจุดกึ่งกลางระหว่างงานสคริปต์กับงานที่หนักขึ้น
    • แนะนำ ImGui binding สำหรับ Python
      ไม่ใช่แบบ retained mode อย่าง Tkinter หรือ Qt แต่เป็นแนว immediate mode เลยเหมาะมากกับเครื่องมือภายใน
      โปรเจกต์ imgui_bundle
  • มีคนตั้งข้อสงสัยว่า “ระดับนี้ไม่น่าจะเป็นแค่โจทย์ optimization ง่าย ๆเหรอ?” และสงสัยว่าทำไม interpreter loop ถึงยังไม่ได้ถูก optimize จนสุด
    บอกว่าคิดไว้ว่าน่าจะถูกเขียนเป็นแอสเซมบลีแยกตาม ISA หลัก ๆ ไปแล้ว
    • อีกมุมกลับมองว่าอัปเดตครั้งนี้ยิ่งแสดงให้เห็นว่า loop นี้ถูก optimize มาอย่างหนักอยู่แล้ว
      [[msvc::musttail]] เป็นแอตทริบิวต์ใหม่มากที่เพิ่งเพิ่มเข้ามาใน MSVC 14.50 (ออกเมื่อเดือนที่แล้ว) และทีม CPython ก็ใช้มันเพิ่มประสิทธิภาพได้ภายในไม่กี่สัปดาห์
      เอกสาร MSVC musttail
    • แต่เดิม Python ก็ให้ความสำคัญกับความเรียบง่ายมากกว่าความเร็ว
      Guido ให้ความสำคัญกับความเรียบง่ายของโค้ด ทำให้การนำ JIT เข้ามาช้า และต่อมาจึงมีความพยายามอย่าง PEP 744 (JIT Compilation)
    • ไม่ควรคาดหวังจากโอเพนซอร์สมากเกินไป
      การ optimize ระดับแอสเซมบลีเป็นฝันร้ายด้านการบำรุงรักษา และคอขวดที่แท้จริงของ Python คือระบบแพ็กเกจ
    • ยังไงก็ตามคนที่ซีเรียสกับประสิทธิภาพก็มักจะไม่รัน Python บน Windowsอยู่แล้ว
  • มีคนถามว่า “ทำไม Python interpreter ถึงช้ากว่า V8 มาก?”
    • JavaScript ใช้JIT compilation แต่ CPython ไม่ใช้
      PyPy เร็วเพราะมี JIT แต่เข้ากันไม่ได้กับ C extension
      โมเดลเธรดของ Python ก็ทำให้การ optimize ยากขึ้น
      ขณะที่ JS เป็น single-thread จึงง่ายกว่า
      Python ยังมีทางอ้อมไปใช้ C extension ได้ จึงมีแรงกดดันน้อยกว่าที่จะต้อง optimize ตัว CPython เอง
    • มองว่าความต่างใหญ่คือกำลังคนและคุณภาพงานของ Google
      อีกทั้ง CPython ยังติดข้อจำกัดเรื่องecosystem ของ C extensionขนาดใหญ่ จึงทำลายความเข้ากันได้ไม่ได้
      ส่วน V8 เปลี่ยนโครงสร้างภายในได้อย่างอิสระกว่า
    • Python มีความไดนามิกกว่ามาก แม้แต่การเข้าถึงพร็อพเพอร์ตีก็ยังผ่าน descriptor protocol
      และยังต้องรักษา C ABI ที่เสถียร ทำให้ JIT วิเคราะห์โค้ดได้ไม่อิสระนัก
    • Python เป็นภาษาที่ไดนามิกกว่า JS มาก และการมี FFI binding ก็จำกัดการเปลี่ยนแปลงภายใน
      มีการยกตัวอย่างว่า PyPy ก็ต้องลำบากมากในการรองรับข้อจำกัดเหล่านี้
    • JS ถูก Google ทุ่มทรัพยากรมหาศาลเพื่อ optimize สำหรับการครองเว็บ แต่ Python ใช้ทรัพยากรไปกับการพัฒนาภาษามากกว่า
      อีกทั้ง JS รองรับแค่ JS ล้วนก็พอ แต่ Python ต้องรักษา ecosystem ของ extension ภายนอกไว้ จึงมีข้อจำกัดในการ optimize มากกว่า
  • มีคนบอกว่ากราฟ benchmark แบบใหม่ดูน่าสนใจและถามว่าใช้เครื่องมืออะไรสร้าง
    พร้อมแชร์ว่าตัวเองเคยใช้ mitata วัดประสิทธิภาพไลบรารี JS มาก่อน
    PR การ optimize ของ Immer JS
  • มีคนเล่าว่า Matt Godbolt เคยพูดว่าอินเทอร์พรีเตอร์ที่อิง tail-call เข้ากับbranch predictor ของ CPU ได้ดีกว่า
  • มีคอมเมนต์ถามว่าประโยคแรกของบทความมีคำผิดสองจุด เป็นความตั้งใจเพื่อให้ดูเหมือนไม่ได้ใช้ AI เขียนหรือเปล่า
    • ผู้เขียนตอบว่า “ขอบคุณ แก้แล้ว”
    • ผู้ใช้อีกคนแซวว่า ถ้าไม่อยากให้ดูเหมือน AI ก็แค่เขียนเอง
      พร้อมล้อเลียนลักษณะเด่นของข้อความจาก AI เช่นย่อหน้าสั้น น้ำเสียงเชิงบวกเกินไป และเนื้อหาไม่ลึก
  • มีคนถามว่า “ถ้าทีม Python เห็นว่า tail call มีประโยชน์ แบบนี้จะมีการรองรับ tail call ในตัวภาษาเองหรือไม่?”