free-threaded Python 以惰性檢視解析 JSON 達 65.7 GB/s
Daniel Lemire 部落格 · 2026-10-12 · fastsimdjson 0.6.0
Python 解析 JSON 的瓶頸,從「解析器不夠快」變成「建立 Python 物件太貴」:fastsimdjson 的惰性檢視(lazy view)只為被存取的欄位建物件,在 free-threaded Python 3.14 上用 64 執行緒,達到 65.7 GB/s。
這是 simdjson 作者 Daniel Lemire 在 2026-10-12 公布的基準測試,使用他維護的 Python 綁定 fastsimdjson 0.6.0。數字全部來自作者自己的測試環境,並非第三方重現。
原本的問題
CPython 長期受全域直譯器鎖(GIL)限制,同一時間只有一個執行緒能跑 Python 程式碼,多核心對 JSON 解析幫助有限。Python 3.14 提供 free-threaded 版本(python3.14t),移除了 GIL,多個執行緒終於能同時跑 Python 程式碼。
但只拿掉鎖還不夠。標準庫 json、orjson,甚至 fastsimdjson 非惰性模式,都會把整份文件轉成 Python 物件,而多數物件馬上就被丟棄。作者指出,這些方法的大部分時間花在建立物件上,而不是解析。
測試設計與惰性檢視
題目是統計 GH Archive 的事件:取 2024-03-13 的八小時資料,解壓後 7.6 GB,共 2.2 million 筆事件,每筆約 3.4 kB。程式要數出各事件類型,並對每個新開的 pull request 查出目標儲存庫的語言。結果是 65,269 個 pull request,其中 TypeScript 最多(11,565)。
fastsimdjson 的 map_many 對每筆事件呼叫使用者函式,並可指定執行緒數。傳入 lazy=True 時,函式拿到的是惰性檢視:事件已被解析,但只有被存取的值才會建立 Python 物件。這個做法最早由 Tyler Kennedy 在 pysimdjson 中發展。
fastsimdjson.map_many(one_event, data,
format="lines", threads=64, lazy=True)每個執行緒各自持有一組計數器,執行緒之間不共享,因此不必互相等待;全部完成後主執行緒再合併,作者說合併成本相對於解析可忽略。
實際效果
測試機為兩顆 Intel Xeon Gold 6548N(64 核心),Python 3.14.6。比較對象含 orjson 3.13.0 與 msgspec 0.22.0,其中 msgspec 預先宣告只需要的欄位,作者說這是讓它占便宜。單核心結果如下。
| 方法(單核心) | GB/s |
|---|---|
| json(標準庫) | 0.18 |
| orjson | 0.42 |
fastsimdjson map_many | 0.63 |
| msgspec(型別結構) | 1.29 |
fastsimdjson map_many(lazy) | 2.13 |
標準庫跑完八小時資料要 42 秒,惰性模式只要 3.6 秒。換成 64 執行緒後差距更大。
| 方法(64 執行緒) | GB/s |
|---|---|
| json(標準庫) | 3.17 |
| msgspec(型別結構) | 24.6 |
fastsimdjson map_many | 29.8 |
fastsimdjson map_many(lazy) | 65.7 |
7.6 GB 約 0.12 秒處理完,是單核心的 31 倍,也快過 64 執行緒的 msgspec(24.6 GB/s)。作者的標準庫與單執行緒解析器數字是單次執行,其餘取七次最佳值,且資料在計時前已在記憶體中解壓,所以這不含磁碟與解壓時間。
影響範圍
處理大量 NDJSON 日誌、事件串流的批次作業最直接受益,例如只需要幾個欄位的指標彙整。收益主要來自「只讀少數欄位」:若每個欄位都要取用,惰性檢視的優勢會縮小,作者的非惰性數字(0.63 GB/s 單核心)才是參考。
使用上有幾個前提要檢查。第一,要跑 python3.14t,一般帶 GIL 的版本無法取得執行緒擴展的效果。第二,fastsimdjson 的 PyPI 頁面標示提供 Python 3.10 至 3.14 的 wheel,含 free-threaded 3.14,並可作為 json.loads 的替代品,但惰性 parse 與 map_many 是它自己的 API,需要改寫程式。第三,若已用 msgspec 且 schema 固定,單核心 1.29 GB/s 已不差,是否換用要看欄位比例。
基準程式碼公開在作者的儲存庫,可用自己的資料重跑。