vatt'ghern jaskier's ballads
本文 5 個互動圖表在手機上以重點摘要呈現,互動版請以桌面瀏覽器開啟。

一個在 CPython main 裡跑了三個 release cycle 的 JIT,2026 年 6 月 5 日被自己的 Steering Council 按下了暫停鍵。 公告的字面意思很硬:在一份被正式接受的 Standards Track PEP 落地之前,不要再有新的 JIT 開發進 main——「包括新功能、最佳化與效能工作」;六個月內若沒有可接受的 PEP,這份程式碼就得從 main 移除。 一個技術上「about as fast as the existing specializing interpreter」的實驗功能,撞上的不是 benchmark,而是治理流程。

CPython 的 JIT 被按下暫停鍵——一個實驗功能怎麼撞上治理流程

篇講的不是「JIT 快不快」,而是一個更少被攤開講的東西:一個複雜、影響面很廣的功能,當初是怎麼以 experimental 身分、靠一份 informational 的 PEP 併進 CPython 的,三個版本之後又怎麼走到一個「凍結+補一份 PEP+六個月窗口」的治理十字路口。 如果你關心的是 free-threading、profiler、debugger 這些工具未來怎麼跟一個官方 supported 的 JIT 共存,或者你只是想看清楚一個 OSS 專案在「動能」與「流程」之間怎麼拉扯,這篇把時間線、各方立場、與 SC 提出的條件逐一拆開。 我會誠實標示哪些是已經寫進公告的承諾、哪些只是當事人的預期。

先把這個 JIT 的身世講清楚,因為它正是這場爭議的根。 它不是憑空長出來的一塊獨立編譯器——它是長在 CPython 「specializing adaptive interpreter」這套既有機器上的最後一層。 理解這層關係,才能理解為什麼 Mark Shannon 會說「在 fork 裡平行開發 JIT 不太可行」不是耍賴,而是一個真實的工程約束。 下面這張圖把整條 copy-and-patch 管線攤開,每一格的重點不是「畫了哪些 box」,而是「這一段的改動會不會牽動別段」——點任一格看它的職責與它跟凍結令的關係。

點任一格讀它的職責與它跟凍結令的關係 · 5 個階段

copy-and-patch JIT 管線(長在既有 interpreter 之上) Tier 1 · specializing adaptive interpreter 收 runtime profile · 把 bytecode 特化成 adaptive instruction Tier 2 · micro-ops trace 把一條 instruction 拆成更細的 micro-ops(爭議的震央) micro-op optimizer 在 trace 上做常數傳播、去冗餘、type narrowing build-time stencils 每個 micro-op 一塊預編譯模板 patched machine code runtime 把地址/常數燒進指令 注意:所有 CPython build 本來就含這套 micro-op 機器;JIT 只是把它「靜態編出來」省掉 dispatch

Tier 1 · 特化直譯器

這是 CPython 從 3.11 起就有的 specializing adaptive interpreter。它在執行時收集型別、記憶體佈局、執行路徑等 profile,把通用 bytecode 換成特化過的 adaptive instruction(例如把通用的 BINARY_OP 特化成 BINARY_OP_ADD_INT)。

它不知道:自己會不會被 JIT 編譯。Tier 1 本身就能獨立運作,JIT 不在時整套照跑。

凍結令不碰這層——bugfix 與安全修補照常。

Tier 2 · micro-ops

Tier 2 把一條 interpreter instruction 進一步拆成更細的 micro-ops,形成可被最佳化的 trace。PEP 744 點明:所有 CPython build 其實都含「this exact micro-op translation, optimization, and execution machinery」,只是預設關閉,因為純直譯這些 micro-op 的 overhead 太大。

關鍵張力:Mark Shannon 說「we often need to change the micro ops that make up an interpreter instruction to make it simpler to do optimizations in the JIT」——要在 JIT 做最佳化,常得回頭改構成 instruction 的 micro-ops。

這正是「為什麼不能搬去 fork 做」的根:改 micro-ops 同時動到 interpreter 與 JIT 兩邊。

micro-op optimizer

在 micro-op trace 上做最佳化——常數傳播、去除冗餘的 refcount/型別檢查、把已知型別 narrow 掉。這層產出的是「已最佳化的 micro-op trace」,而不是原始 bytecode。

它依賴:上一層 micro-ops 的形狀。一旦 micro-ops 的拆法改了,optimizer 的 pattern 也要跟著調。

「最佳化與效能工作」正是公告明文要凍結的範圍。

build-time stencils(模板)

copy-and-patch 的精髓:每一種 micro-op 在 build time 就由一段 C DSL 編成一塊預編譯的機器碼模板(stencil)。執行期不需要一個傳統意義上的 codegen backend——只要把對的模板挑出來。

它不知道:實際要燒進去的常數與地址是多少;那是下一步的事。模板是「挖好洞的機器碼」。

模板由 build 工具鏈生成,動它牽動建置流程與平台覆蓋。

patched machine code

runtime 把 stencil「copy」一份,再把實際的值、地址、cache 資料「patch」進預留的洞裡——直接燒進機器指令。拼起來就是這條 trace 的原生碼。整體大約比基礎直譯器多用 10–20% 記憶體(上緣來自某些架構較大的 page size)。

現況:在多數平台上「about as fast as the existing specializing interpreter」——一個工程成就,但還不是足以預設開啟的明確勝利。

這就是 SC 的潛台詞:對 3.15 維持現狀(experimental、預設關)是 OK 的。

互動圖表

JIT 所有改動共享同一份 micro-op 定義表,這正是它無法搬到 fork 平行開發的根本原因。

把上面這張圖記在腦子裡,後面所有的爭執都能對得上位置。 copy-and-patch 之所以選在 CPython 內部開發,不是偶然——它的 stencil 由 build 期的 C DSL 生成、它吃的是 Tier 2 的 micro-op trace、它要的最佳化常常得回頭改 micro-ops。 這三件事都把 JIT 跟 interpreter 縫在一起。 Shannon 後面那句「在 fork 平行開發會讓志工貢獻變複雜、產生痛苦的 merge diff」,技術上就是這張圖的直接推論。 現在,故事可以開始了。

身世:一份 informational PEP,把實驗功能放進 3.13 的 main

這個 JIT 的起點不是一份「要不要做 JIT」的決策文件,而是一份說明性質的 PEP 744。 把這點講清楚很重要,因為它是整起事件的制度性根源。 PEP 744 的標題就叫「JIT Compilation」,它的角色是 informational——記錄、描述,而不是要 Steering Council 在「是否接受這個功能成為 CPython 一部分」這件事上做一次正式表決。 换句話說,這個 JIT 從一開始就不是靠「社群討論+SC 接受 Standards Track PEP」這條正規路徑進來的;它是以 experimental 身分,搭著 informational PEP 的順風車,在 3.13 release cycle 裡併進了 main branch。

informational 與 Standards Track 的差別,在 Python 治理裡不是文書分類,而是「要不要被當成 CPython 的正式承諾」的分水嶺。 Standards Track PEP 描述新功能或實作,要進語言或標準庫就得走它,過程含公開討論、SC 表決接受或拒絕——一旦接受,就是社群對「我們會維護它」的集體背書。 informational PEP 只提供資訊、記錄設計,不要求承諾,也不需 SC 接受表決。 PEP 744 走後者:它把 JIT「是什麼、怎麼運作」寫清楚了,卻從沒讓 SC 在「是否把它收為正式維護的一部分」上點頭。 這就是為什麼三個版本之後,「它什麼時候升 supported」會變成一個無人正式授權、卻箭在弦上的問題。下面這個小詞庫先把後面反覆出現的幾個 JIT 名詞釘清楚——把游標移到虛線詞上(手機點一下)看定義。

讀後面前先記住四個名詞:一個 copy-and-patch 的 JIT,吃的是 micro-ops 那一層;它把這個實驗功能 tier-up 成原生碼,而它未來最棘手的鄰居是 free-threading

把機器碼生成搬到 build time:每個 micro-op 預先編成一塊挖好洞的模板(stencil),runtime 只「copy」模板再「patch」進實際的值,省掉傳統 JIT 的 codegen backend。 比 bytecode instruction 更細的操作單元。所有 CPython build 都帶這套 micro-op 機器,只是預設關閉——改 micro-ops 會同時動到 interpreter 與 JIT 兩邊。 把一段熱程式碼從較慢的執行層(Tier 1 特化直譯)升到較快的層(JIT 原生碼)。tier-up 的決策與門檻正是「最佳化工作」的一部分,落在凍結範圍內。 移除 GIL 的 no-GIL CPython。它改的是物件存取的同步,與 JIT 改的執行碼生成在 micro-op 層可能相互牽動——PEP 必須保證兩者不打架。

它為什麼長成「experimental、預設關閉」的樣子?因為它本來就不是一塊全新的編譯器。 前面那張圖已經說明:Tier 2 的 micro-op 機器是所有 CPython build 都帶著的,只是預設不開——純直譯 micro-op 太慢。 JIT 做的事,是把這些「已經最佳化過的 micro-op trace」用 copy-and-patch 的方式靜態編成原生碼,省掉直譯時的 dispatch 與 decode overhead。 所以它與其說是「外掛一個 JIT」,不如說是「把既有的 Tier 2 機器接上一條原生碼出口」。 這個身世決定了它的物理位置只能在 CPython 內部。

copy-and-patch 為什麼「不像傳統 JIT」,這個技術選擇本身就解釋了它跟 interpreter 黏這麼緊。 傳統 JIT(V8 的 TurboFan、JVM 的 C2)會在 runtime 帶一個完整的 codegen backend——幾萬行、要懂每個架構的暫存器分配與指令選擇。 copy-and-patch 走相反方向:把「生成機器碼」這件最重的事整個搬到 build time。 每一種 micro-op 在編譯 CPython 時,就由一段 C DSL 編成一小塊預先挖好洞的機器碼——這塊就是 stencil;洞對應「runtime 才知道的值」:常數、跳轉地址、inline cache 指標。 執行期 JIT 不做指令選擇或暫存器分配,只是把對的 stencil「copy」一份、把實際的值「patch」進洞裡,再把一條 trace 上的 stencil 首尾接起來——名字就是這麼來的。 代價是沒有跨 micro-op 的全域最佳化(那得在上游的 micro-op optimizer 先做完),換來 runtime 那段極度輕薄、好移植。

但這個設計也正是把 JIT 縫死在 interpreter 上的原因。 stencil 是「每個 micro-op 一塊」——它的存在預設了一套固定的 micro-op 定義。 一旦為了開一條新的最佳化路徑而改了某個 micro-op 的拆法(Shannon 說的「change the micro ops that make up an interpreter instruction」),那塊 stencil 的 C DSL、micro-op optimizer 對它的處理、以及 Tier 1/Tier 2 直譯這個 micro-op 的程式碼,全都得一起改。 這不是「JIT 的程式碼」與「interpreter 的程式碼」可以乾淨切開的兩坨——它們共享同一份 micro-op 定義表。 把這層關係記牢,後面 Shannon「不能搬去 fork 做」的主張就完全是技術上的必然,而不是立場。

三個 release cycle 過去——3.13 併入、3.14、到正在收尾的 3.15——它一直是 experimental。 效能上,它在多數平台「about as fast as the existing specializing interpreter」,記憶體多用約 10–20%。 對一個還在演進的功能,這是合理的中間態:證明了 copy-and-patch 在 CPython 裡跑得起來,但還沒到「快到值得預設開啟」的程度。 問題是,experimental 不會自己停在原地——遲早有人要問:它什麼時候要從 experimental 升成 supported?升上去之後,誰負責長期維護?它跟 free-threading、profiler、debugger 怎麼共存?而這些問題,沒有一份被 SC 正式接受的 Standards Track PEP 在背書。 這就是 6 月 5 日那份公告要補的洞。

下面這條時間軸把「實驗功能的演進」與「治理流程的介入」疊在同一條線上——拖動把手,看這個 JIT 在哪個時間點撞上 SC。

拖把手沿時間軸看每個節點的狀態 · 7 個事件

JIT 專案時間軸——實驗演進 vs 治理介入 3.13

互動圖表

凍結令在 3.15 beta cutoff 後才發出,讓 3.15 維持現狀,六個月窗口針對的是未來的 main。

觸發點:SC 為什麼選在 3.15 beta cutoff 之後出手

時機是這個故事裡最被刻意設計的一環。 SC 沒有在隨便一個週二發這份公告,而是等到 3.15 的 beta cutoff 之後。 Thomas Wouters 把理由講得很白:「The timing is because we wanted to be clear that we're okay with the status quo for 3.15, so we were waiting until after the beta cutoff.」翻成工程語言:在 beta cutoff 之後,3.15 的功能集已經定型,JIT 維持 experimental、預設關閉的現狀已成定局。 SC 想傳遞的訊號是——「我們不是在恐慌中要把 JIT 從 3.15 拔掉;3.15 就照現在這樣出,沒問題」。 動作的對象是 main(也就是未來的 3.16),不是正在發的 3.15。

這個時機選擇本身就是對「動能顧慮」的回應:在 release 即將定版時出手才真會嚇跑貢獻者,選在 cutoff 之後至少把「即將發布的版本」這個變數從等式裡拿掉了。

公告另一個值得記下的部分,是 SC 對自己的檢討——這不是一份把責任推給 JIT 開發者的文件。 SC 明說:「collectively we (the Steering Council) have not been as strict about following the process as a change of this complexity and reach deserves.」——是 SC 自己當初遵循流程不夠嚴格,才讓一個本該走 Standards Track 的功能靠 informational PEP 進來。 這份凍結令與其說在懲罰 JIT,不如說是 SC 在補自己當年沒走完的程序;它把「治理 vs 技術」的對立,部分轉回成「治理對自己的修正」。

條件:凍結令、一份 Standards Track PEP、六個月窗口

在拆條件之前,先把「成功指標」那道題量化一下,因為它是這場討論裡少數能用數字逼問的部分。 PEP 744 對現況的描述相當誠實:JIT 在多數平台「about as fast as the existing specializing interpreter」——目前最好的成績是「跟不開 JIT 的特化直譯器打平」,而非明顯更快;記憶體則多吃約 10–20%。 一個 supported 功能預設開啟的理由通常是「它讓多數程式更快」,但現在的它是「速度持平、記憶體更貴」。 SC 要 PEP 立「clear, measurable success metrics and timelines」,等於逼問:要快多少、在哪些平台、付多少記憶體代價,才值得把它從 experimental 推成預設?這條線同時是給開發者的目標、也是 SC 未來表決的依據——而它目前還是空白。

決定的內容由三塊組成,得拆開看,因為每一塊的「硬度」不一樣。

第一塊,凍結令。 字面是:「until such a PEP is accepted, we ask that no new development on the JIT land on main」。 範圍由公告明文界定——「包括新功能、最佳化與效能工作」。 但同一句話也留了出口:「Bugfixes and security fixes may of course continue.」所以這不是把整塊程式碼冷凍,而是凍住「讓它變更好/更快/更大」的那一類改動,同時讓「讓它不壞、不被攻擊」的那一類照常。 對照前面那張管線圖:optimizer 的最佳化、改 micro-ops 來開新最佳化路徑、效能 tuning——這些都落在凍結範圍內;JIT 程式碼裡的 crash 修復、安全修補不在。

第二塊,一份 Standards Track PEP。 這是整件事的核心要求,也是唯一能解凍的鑰匙。 SC「formally requesting a Standards Track PEP be authored」——一份要經社群討論、由 SC 正式接受(或拒絕)的提案,把 JIT 從 experimental 升為 supported,補上當年沒走的那道程序。 下面這張圖把兩條治理路徑並排:JIT 當初走的「informational 順風車」與 SC 現在要求的「Standards Track 正門」,差別不在文件格式,而在中間那道 SC 表決閘門——它是整起事件的制度斷點。

兩條路徑:灰色順風車 vs 正門表決 PEP 744 走過的路(informational) SC 現在要求的路(Standards Track) 寫一份 informational PEP 記錄「是什麼」,不求承諾 (無 SC 接受表決) 沒有集體維護承諾 experimental 進 main 3.13 起,預設關閉 升 supported? 卡住——無人正式授權 寫一份 Standards Track PEP 立指標、相容、維護承諾 公開社群討論 六個月窗口內完成 SC 表決:接受 / 拒絕 ← 整起事件補的那道閘門 supported(或被退回) 有集體維護背書 左路缺的那一格(SC 表決),正是右路的核心——凍結令逼的就是補上它
同樣是 PEP,差別在中間那道 SC 接受表決——左路從來沒有,這就是為什麼「升 supported」三年來無人能正式授權。

同樣是 PEP,差別在中間那道 SC 接受表決——左路從來沒有,這就是為什麼「升 supported」三年來無人能正式…

informational PEP 讓 JIT 進了 main 卻跳過 SC 表決,這正是三年後必須補 Standards Track PEP 的制度根源。

這份 PEP 具體要回答哪五類問題,本節稍後用一張表攤開。

第三塊,六個月窗口。 這是最容易被誤讀成「硬死線」的部分,但當事人特別澄清過。 SC「setting a window of six months for a PEP to be submitted and resolved」,若未達成,「the JIT code must be removed from the main branch」、開發轉到主 repo 之外。 聽起來很硬。 但 Galindo 補上:「The SC will be flexible of course. If we see the PEP is stuck on the discussion or on the SC queue we are obviously not going to trigger that.」——六個月是錨點,不是斷頭台;如果 PEP 是卡在討論或 SC 自己的審查佇列上,他們顯然不會去觸發移除。 這裡要誠實標示:六個月+移除是寫進公告的承諾;「會彈性處理、不會機械式觸發」是 Galindo 的口頭說明與預期,不是公告裡白紙黑字的條款。 讀者該把前者當約束、把後者當善意但非保證的解釋。

那麼 SC 要的這份 PEP,到底得回答哪些問題?公告列了五類,每一類背後都有具體的工程難點。下表把它們並排——切到不同分頁,看每一類「要證明什麼」與「難在哪」。

SC 公告要求這份 PEP 必須回答的五類問題——以及每一類為什麼不只是「寫一段話」就能交差。
要求面向PEP 要證明什麼難點在哪
維護與永續性 長期維護承諾;對更廣貢獻者社群的影響,而非只有 JIT 專家撐得起。 改 micro-ops 同時動 interpreter 與 JIT,志工難以參與——這正是 Shannon 點名的 bus-factor 問題。
相容性保證 與 free-threading、profiler、debugger 等工具的互動如何保證不破。 JIT 把 micro-op trace 編成原生碼後,debugger 的 frame 還原、profiler 的歸因都得重新對齊。
成功指標 「clear, measurable success metrics and timelines」——效能 benchmark、平台覆蓋、記憶體 overhead 與時程。 現況是「約等於特化直譯器」、記憶體 +10–20%;要立一條「快多少才算 supported」的線並不容易。
與其他 JIT 的關係 跟 CinderX、Numba、PyTorch 編譯這些既有 JIT/編譯路徑的關係與分工。 生態裡已有多個 JIT;官方 JIT 的定位是替代、互補還是並存,會影響整個社群的投資方向。
架構穩定性 copy-and-patch 架構本身是否已經穩到值得做 supported 承諾。 stencil 由 build 期 C DSL 生成、綁 micro-ops 形狀;架構仍在演進,「凍結」反而讓它更難證明穩定。

互動圖表

SC 要求 PEP 回答五類問題:維護永續、相容性保證、可量化成功指標、與既有 JIT 的定位、架構穩定性。

「與其他 JIT 的關係」那一列值得單獨拆一段,因為它把這份 PEP 從「CPython 內部事務」拉成生態定位問題。 Python 世界早就不只一個 JIT/編譯路徑:Meta 開源的 CinderX 帶著自己的方法 JIT;Numba 對數值程式碼做 LLVM-based 即時編譯;PyTorch 的 torch.compile 為張量運算另闢 trace 與 fusion。 它們各自服務不同工作負載、井水不犯河水,是因為都不是「CPython 官方預設的那一個」。 一旦 copy-and-patch JIT 升 supported,它就有了官方背書的特殊地位——會不會吸走投向 CinderX 的注意力?SC 要 PEP 講清楚的,本質上是策略問題,而策略問題通常比 benchmark 更難在六個月內取得共識。

這張表最後一列藏著整起事件最尖銳的循環論證:SC 要 PEP 證明「架構穩定」,凍結令卻同時禁止「最佳化與效能工作」——而架構往往正是靠繼續做最佳化才會收斂穩定。 等於要求開發者「在不准打磨的前提下,證明這塊東西已經夠光滑」。 Ostrowski 的回應(不會只用今天的狀態評斷)把標準從「現況快照」挪向「設計與承諾」,部分緩解了悖論,但沒消除它:架構穩定性終究有一部分只能靠「再跑幾個 cycle 的最佳化」才看得出來,而那正是被凍結的。 這就把我們帶到張力的核心。

張力:Shannon 的動能顧慮,對上 Ostrowski 的流程價值

同一份公告,社群讀出了兩種完全不同的東西。把雙方立場並排看最清楚——切到不同分頁,讀每一方說了什麼、為什麼。

切分頁比較四方立場 · 4 個 tab

Steering Council · 立規矩的一方

Pablo Galindo Salgado(代表 SC 發文)、Thomas Wouters

核心訴求:一個這種複雜度與影響面的功能,要從 experimental 升 supported,必須走完「社群討論 → Standards Track PEP → SC 正式接受」這條程序。立場帶著自我檢討——「collectively we (the Steering Council) have not been as strict about following the process as a change of this complexity and reach deserves.」

對六個月的態度是錨點而非死線(Galindo:會彈性處理、不會在 PEP 卡審查時機械式觸發移除);但對「先給寬限期再說」的呼聲,SC 拒絕了。

Savannah Ostrowski · 支持立即凍結

Savannah Ostrowski

論點:現在就公開討論,反而能形塑 PEP,而不是反過來。「Kicking off the community discussion now should actually help shape the PEP rather than the other way around.」

她也回應了「現況不夠好就會被打回票」的擔憂——PEP 不會只用「今天的狀態」來評斷(the PEP won't be evaluated solely on where things stand today)。換句話說,凍結不是對現有成果的否定,而是把討論提前。

Mark Shannon · 反對

Mark Shannon(JIT 核心貢獻者)

顧慮一:動能與人。停掉所有開發直到 PEP 被接受,會「失去動能、失去最近才加入的新貢獻者」。

顧慮二:技術上根本難以「搬去別處做」。「we often need to change the micro ops that make up an interpreter instruction to make it simpler to do optimizations in the JIT... It is possible to do for full time developers, but isn't really viable for volunteers.」改 micro-ops 同時動 interpreter 與 JIT——全職開發者做得到,志工不行。這也是 JIT 當初選在 CPython 內開發的原因;在 fork 平行開發會讓志工貢獻變複雜、產生痛苦的 merge diff。

Diego Russo · 接下統籌

Diego Russo

實務上把球接住的人。他自願統籌 PEP 撰寫、並要組一個工作小組:「I'm happy to help coordinate the writing of that PEP.」、「Next week I'll start putting things together for having a discussion... I already have a few people who expressed interest to me.」

這一方不站「該不該凍結」的隊,而是把爭論導向「那就把 PEP 寫出來」——某種意義上,他的出現是讓 SC 的條件有機會被滿足的關鍵。

互動圖表

SC 要求 Standards Track PEP、Shannon 憂動能損失、Ostrowski 信流程能形塑 PEP、Russo 統籌撰寫。

Shannon 的反對值得認真對待,因為它不是情緒,是把前面那張管線圖反過來唸一遍。 JIT 在 CPython 內開發,正是因為「做 JIT 最佳化常常要回頭改 micro-ops」,而 micro-ops 是 interpreter 與 JIT 共用的那一層。 搬到 fork 平行開發,每一次為 JIT 動 micro-ops 都會製造一道 fork-main diff;志工得追著 upstream rebase,merge 衝突變成日常——這不是難度問題,是「誰有時間天天追 rebase」,通常只有全職開發者扛得住。

Ostrowski 的支持則是另一個維度的真話。 她沒否認 Shannon 的技術顧慮,而是指出:與其讓 JIT 繼續長到「升 supported」變成既成事實逼 SC 表態,不如現在就把討論攤開,讓社群意見有機會 形塑 這份 PEP。 她那句「PEP 不會只用今天的狀態來評斷」,正面回應了 Shannon「凍結會讓 PEP 拿著不夠好的快照送審」的隱憂。 兩人其實在講同一件事的兩面:一個怕流程殺死動能,一個信流程能引導動能。

而 SC 站在中間,做了一個不討喜但有內在一致性的選擇——拒絕「先給寬限期」。 因為寬限期本質上就是「再讓它以現狀繼續長一陣子」,那恰好是把「靠 informational PEP 就 experimental 進來」的歷史再延長一次。 SC 既然已經承認當初程序沒走嚴,給寬限期等於重蹈覆轍。 從這個角度,凍結令的硬,是對自己過去軟的矯正。

後果與未解:free-threading、誰寫 PEP、JIT 的未來

公告落地之後,有些事定了,有些事還懸著。把它們分清楚,是這篇敘事該交付的東西。

定了的(承諾)。 凍結即日生效,範圍是新功能、最佳化、效能工作;bugfix 與安全修補不在內。 3.15 照現狀出,JIT 維持 experimental、預設關閉。 SC 要求一份 Standards Track PEP,六個月窗口,未達成則 JIT 從 main 移除、開發轉出主 repo。 Diego Russo 接下 PEP 統籌、要組工作小組。 這些都是白紙黑字或當事人公開承接的。

還懸著的(預期,非保證)。 六個月會不會真的彈性延長,取決於 SC 屆時的判斷——Galindo 的「不會在 PEP 卡審查時觸發移除」是善意說明,不是公告條款。 PEP 能不能在窗口內寫出讓 SC 接受的版本,取決於那個還沒成形的工作小組。 最關鍵也最具體的技術未解,是相容性那一欄:一個 supported 的 JIT 要怎麼跟 free-threading(no-GIL)共存?兩者都在改 CPython 最底層的執行機制,free-threading 動的是物件存取的同步、JIT 動的是執行碼的生成,它們在 micro-op 這一層會不會打架,PEP 必須給出保證——而這恰恰是「凍結最佳化工作」之後最難實驗的部分。 profiler 與 debugger 怎麼在原生碼 trace 上還原出開發者預期的 frame,同樣懸而未決。

還有一個更微妙的未解,是 Shannon 點出的人的問題會不會成真。如果六個月裡 JIT 真的「失去動能、失去最近才加入的新貢獻者」,那麼就算 PEP 最後被接受,接得住長期維護的人是否還在?這個顧慮無法用條款回答,只能等時間驗證——而它恰好就是 PEP「維護與永續性」那一欄要回答、卻最難用文件保證的東西。

「移除到 main 之外」那條後路值得具體想一遍,因為它是公告最硬、也最容易被低估後果的條款。 假設六個月過去、PEP 沒被接受,JIT 被請出 main、開發轉到獨立 repo——這時 Shannon 描述的痛苦才真正開始。 JIT 與 interpreter 共享同一份 micro-op 定義;一旦分家,兩邊的定義會持續漂移,merge diff 隨時間單調變胖。 對全職開發者這是「煩」,對志工是「勸退」。 所以這條後路不只是「換個地方放程式碼」,技術上幾乎等同於宣告這個 copy-and-patch JIT 的志工貢獻模式結束。

那麼一個下週還是得照常 ship Python 程式碼的工程師,該拿走什麼?短期內什麼都不用改:JIT 在 3.15 維持 experimental、預設關閉,你的 production CPython 跟昨天沒兩樣,不必動任何 build flag 或部署設定。 真正該做的是校準預期:roadmap 裡若有「等官方 JIT 預設開啟、白拿一波加速」,把它從「3.16 大概就有」往後挪——這功能現在卡在治理流程,最快解鎖是一份還沒動筆的 PEP 過審。 若要把熱路徑賭在加速上,現階段更穩的仍是定位明確的專用方案(數值走 Numba、張量走 torch.compile),它們的維護承諾不在這場凍結的不確定性裡。

把整起事件抽到最高處看:這不是「JIT 好不好」的技術裁決,而是一個 OSS 治理機構在「動能」與「程序正當性」之間的取捨,且取捨對象包括它自己。 賭注是:用六個月的動能損失,換一個「升 supported 之前先把維護、相容、指標講清楚」的契約——划不划算,要看那份還沒寫出來的 PEP,以及六個月後 JIT 開發者社群還剩多少人。

What changes:對 CPython 來說,這把「experimental 能不能靠 informational PEP 偷渡成 supported」這條灰色路徑正式關上了——以後這種量級的功能,得先有一份被 SC 接受的 Standards Track PEP 才算數;對你我這種等著用 JIT 的人,未來半年該盯的不是 benchmark,是那份 PEP 怎麼回答 free-threading 與長期維護這兩道題。