世界を動かす技術を、日本語で。

Python 3.15のJITが再び軌道に乗りました

概要

  • CPython JIT が目標より早くパフォーマンス向上を達成
  • コミュニティ主導 とチームワークが成功要因
  • 技術的転換点 はトレース記録や参照カウント削減
  • インフラ整備 と日々のフィードバックの重要性
  • 人との交流 や運の要素も大きな役割

CPython JITの進捗と成果(2026年3月時点)

  • macOS AArch64 でJITはインタプリタ比約11-12%高速化
  • x86_64 Linux でも標準インタプリタ比5-6%高速化
  • 3.15 alpha JIT の数値は幾何平均・暫定値
  • パフォーマンス幅 は20%低下から100%以上の高速化まで
  • free-threading 対応は3.15/3.16で目指す
  • JIT開発 は一時困難だったが、再び軌道に乗る

JITプロジェクトの苦難と転機

  • 3.13/3.14 JIT はほぼ速度向上なし、むしろ遅かった
  • Faster CPythonチーム は資金提供者を失い、未来が不透明に
  • 主要貢献者 (Savannah Ostrowski, Mark Shannon, Diego Russo, Brandt Bucher, Ken Jinなど)が継続
  • 運とタイミング、適切な人材が揃ったことが成功の鍵
  • 貢献者拡大 のため、作業分割と明確な手順書を用意

コミュニティ主導による開発体制強化

  • バス係数低減 を目指し、各段階で2名以上のアクティブメンバー配置
  • 中間処理(middle-end) の貢献者が2名から4名に増加
  • Brandt による問題分割と「最適化タスクの細分化」
  • 新規貢献者 が参加しやすい環境整備
  • 11名の貢献者 がインタプリタ命令の変換作業に参加
  • 成果の可視化 と達成の祝福がモチベーション向上に寄与

技術的ブレークスルーと幸運

  • トレース記録型JIT への転換が大きな効果
    • Brandt の提案から始まり、初期は失敗だったが設計変更で成功
    • dual dispatch 方式でインタプリタの肥大化を回避
    • JITコードカバレッジ が50%増加し、最適化の効果が倍増
  • 参照カウント削減 の最適化
    • Matt Page の先行研究を基に、分岐削減で大幅な高速化
    • 教育的価値 も高く、新規貢献者の学習機会となる

インフラと日常的なフィードバック

  • Savannah が実質一人でインフラを担当
  • 4台のマシン で日常的なJITパフォーマンス計測
  • パフォーマンス回帰 の早期発見と最適化効果の可視化
  • ARM対応 やプロファイラ対応など多様な技術課題にも挑戦

人とのつながりと学び

  • PyPy 開発者CF Bolz-Tereickとの交流
  • Max Bernstein らとのコンパイラ談話でモチベーション維持
  • 他プロジェクト参照 による視野拡大と技術力向上
  • 知識やアイデアの共有 がプロジェクト成功に不可欠

結論:人と運がJITを動かす

  • 人の力幸運 がCPython JITの進化を支える
  • JIT go brrr (JITが速く動く!)

Hackerたちの意見

まだ適切なフリースレッドサポートはないけど、3.15/3.16でそれを目指してるんだ。JITは今、軌道に戻ったよ。最近、フリースレッドを実装してエコシステムを通じて修正を加えることについてのインタビューを読んだんだけど、彼は「3.16か3.17ではフリースレッドビルドだけになることを願っている」って言ってた。JITにもそれが適用されるべきなのか、JITとインタプリタの関係がどうなるのか気になるな。

フリースレッディングはパフォーマンスを悪化させるだけで、助けにはならないと思ってるし、Pythonはそれをやめるべきだね。1%のユーザーのためにスレッドセーフなコードをあちこちに書かなきゃいけないのはおかしいよ。サブインタープリターが使えない理由があるにしてもね。

PyPyにはすでにJITコンパイラがあるんじゃないの?なんでそれを使わないの?

確か、PyPyは全てのCPython拡張をサポートしてないから、純粋なPythonコードは多分(ほぼ確実に)問題なく動くけど、他のものはほとんど期待できないと思う。PyPyは3.11までしかサポートしてないんじゃなかったっけ?

PyPyはもう機能してないみたいだから。しばらく更新されてないよ。例えば、ここを見てみて:https://github.com/numpy/numpy/issues/30416。新しいPythonバージョンとの互換性のために更新されてないんだ。

なんでリファレンス実装にJITを追加しないべきなの?他の実装にすでにあるからって理由にはならないよ。それはCPythonにすでに存在するからリスト内包表記をスキップするようなもんだ。

流行だった時にPyPyやPEP 399を支持することが大事だって騒いでた人たちが、今は企業から「PyPyなんて関係ない」って言われてるのが面白いよね。CPythonは今流行ってるものや、雇用主が求めるもの、利益が出るものにしか動かないんだ。

PyPyは資金や貢献者が足りなくて、メンテナンスモードに制限されてるんだ。過去には、少数の貢献者や資金が「マイナー」なPyPyのバージョンを推進するのに役立ったと思う。PSFが捨てた連邦資金をPyPyが受け取れなかったのは残念だね。

PyPyはCPythonじゃないからね。多くのPythonコードはまだCPythonの内部やC拡張、デバッガー、変なプラットフォームの挙動に依存してるから、PyPyはそのギャップがサポートの問題になるまで動くんだ。JITはホットループでは役立つけど、混合ワークロードの場合、ウォームアップコストや互換性の問題があって、ほとんどのチームは最初に依存関係がターゲットにしてるインタープリターを使い続けるんだよね。

JITに関することを把握するためにPRやイシューのトラッカーを時々チェックしてるけど、高レベルの議論がどこで行われてるのか見たことがない。イシューやPRはいつも詳細に飛び込んじゃうから。トレース投影と記録の違いについての高レベルの紹介や例がどこかにある?ググるとCPythonのイシュートラッカーが最初に出てくることが多いし、リポジトリのjit.mdは比較的シンプルで、あまり更新されてない :( 同じように、参照カウントの排除についても完全には理解してない。コード生成の違いは見たことあるけど、コード生成はビルド時に行われるから、各オペコードが削除されたインクリフやデクリフありなしで2つ(またはそれ以上?)のステンシルに分かれる可能性があるってこと?オペコードとその専門的なバリエーションがこんなにたくさんある中で、今はどれくらいのステンシルがあるんだろう?

PEPを見てみるといいかも。自分はこのトピックを深く掘り下げてないけど、関連してるっぽいよ:https://peps.python.org/pep-0744/

更新: 質問を誤解してた :-/ これ無視しても大丈夫だよ。コンパイラで遊ぶのが好きだから、ちょっと説明できるかも。みんなのために簡単に説明するね(スタックは無視するけど): Pythonで関数間でオブジェクトが渡されるとき、コピーされるんじゃなくて、オブジェクトのメモリアドレスへの参照が送られるんだ。この参照はオブジェクトのデータへのポインタみたいなもので、オブジェクトのメモリアドレスが書かれた付箋みたいな感じ。関数が参照を使って戻るたびに、その付箋を捨てることを想像してみて。オブジェクトがゼロの参照になると、メモリから解放されて再利用できるんだ。だから、参照の数、つまり「参照カウント」を常に正確に保つことは大事なんだ。これがメモリリークの原因になることが多いけど、スピードアップには結びつかないと思う(GCを置き換えるなら別だけど)。

開発者のメーリングリスト読んだことある?そこでPythonの開発者たちがたくさん話し合ってるよ。

Pythonのコードベースには何があって、他のコードベースよりも実装がこんなに難しいの?Ruby、PHP、JSは、みんなかなり短期間でJITを追加してるのに。PythonのJITはもう20年くらい求められてるよ。

いくつかの言語は、機械語にうまくコンパイルするのがすごく難しいんだよね。どんな言語でも大きな要因は、静的型がないことや高い「型の不確実性」、他の動的言語の特徴、維持しなきゃいけない非効率な拡張インターフェース、変わったスレッドモデルとかかな。

PHPやJSは、大手テック企業が資源を投入して速くするために頑張ってたよね。

もうすぐ20年になるPyPyのこと、忘れちゃってるの?

PythonのC APIは内部の構造が漏れちゃってるんだ。内部表現の多くが拡張のために公開されていて、今や基本的に何かを変更すると後方互換性が壊れるのが確実なんだよね。

お金。

まあ、RubyのJITは色々な実装があって、Railsとの互換性に苦労してたし、実際に何人かの人の博士論文の研究を使ってたんだよね。簡単なことじゃなかったよ。

PHPはまだJITを出してないと思ってたけど(デフォルトでは無効になってる設定のことね)。

良くも悪くも、彼らはずっと既存のパフォーマンスを落としたくないっていう姿勢を貫いてるんだよね。それがGILが長い間存在してた理由でもある。

いやー、Python 2から3への移行は本当に大きな変化だったよね。ほぼ5年近くかかったし、主に表面的な構文の変更だけだったのに。ABIを壊して、内部のことを進めるべきだったと思う。多分、他の低レベル言語と統合するための新しくて厳密なAPIを考えたんだろうから、これからはPythonの内部をもっと自由に変更できるようにして、全てを壊さずに済むようにすればよかったのに。

そうだね。大きな変化ではなかった。努力する価値もほとんどなかったよ。

テキストエンコーディングのことは、壊れる可能性を考えると小さな変更じゃなかったよね。しかも、移行やアップグレードにお金がかかるソフトウェアのこともあるからね。僕はまだ2.xのPythonコードベースをいくつか維持してるけど、移行するのにすごくお金がかかるし、顧客はその投資をしたがってないんだ。君の言うことには同意するよ(痛みを伴うならやってしまえっていうのはね)、でもエコシステムの反応を誰も予測できなかったと思う。内部をもっと自由に変更できるっていう最後のポイントも理論上は素晴らしいけど、実際にはとても難しい(もしくは不可能)と思う。どうだろう。無料でオープンソースの小さなプロジェクトを維持していた経験から、そういう立場から来る敵意や権利意識を見てきたよ。あれはPythonのようなものに比べればほんの微塵に過ぎないから、コアチームはできる限り頑張ってると思う。やるも地獄、やらぬも地獄だったんだよね。

でも、誤解してしまって、もっと極端なバージョンを考えちゃったんだ。普通の指示のバージョンを追跡する代わりに、追跡専用の指示を一つだけ作って、二つ目のテーブルの全ての指示がそれを指すようにしたんだ。うん、ここはちょっと混乱するかもしれないけど、いつかもっと上手く説明できるように頑張るよ。これが本当に良い選択だったんだ。最初の二重テーブルアプローチは、インタプリタのサイズが倍になっちゃって、コンパイルされたコードが膨れ上がって、当然遅くなってしまったから。 > 一つの指示と二つのテーブルだけを使うことで、インタプリタのサイズは1つの指示分だけ増えるし、基本のインタプリタも超速いままなんだ。この仕組みを「デュアルディスパッチ」と呼んでるんだ。いつかもっと分かりやすい説明を書いてくれることを願ってるよ。これだけでもかなり興味深いからね。

Pythonにはずっとこれが欲しかったけど、今は機械がコードを書く時代だから、Pythonのような言語はあまり必要なくなる気がする。人間のために作られてるからね、機械のためじゃない。もし機械が面倒な作業をするなら、スリムで速く、厳密に検証されたものを生み出してほしいな。

先日考えてたこととほぼ同じだな…今やCodexが書く時代だから、Goに切り替えてウェブバックエンドの作業をするのも、古臭い部分にイライラせずに、かなりの実行性能を得られるかもしれないし、比較的読みやすい言語のままでいられるかも。

すごい仕事をありがとう!ちょっと初心者的な質問なんだけど、これで資金を戻せるんじゃない?それともボランティアだけで進めるよりは好ましくない方法なのかな?ボランティアが集まって、タスクを分担して、仕事を進める状態にプロジェクトを持っていくのは大変なことだよね?これはPythonのJITに関することで、僕はほとんど何も知らないけど、ほとんどのアプリケーション開発者も同じだろうし、これがどれだけ難しかったかを物語ってるよね。

これで資金が戻ってくるんじゃない?資金はマイクロソフトがチームの大部分を雇ってたからね。彼らは解雇されたか、少なくとも別のプロジェクトに移されたみたいだけど、どうやらAIに取り組んでなかったかららしい。

ARMがその人たちをたくさん引き取って、この仕事を続けるためにお金を払ってるみたいだね。

JITの開発者が、JITの機能を妨げるPythonの特徴について何か言及できるか気になるな。以前のKen Jinのブログでは、__del__が参照カウントの最適化を複雑にするって書いてあったよ。Pythonは、TypeScriptよりも最適化が難しいっていう話があって、Pythonの柔軟性やC APIが関係してるみたい。もし問題のあるPythonの特徴のリストがあれば、プログラマーはそれを避けることができて、JITを有効にできるときにその特徴が使われていないことを証明できるかもしれない。これが今のPythonのJITが難しい状況から抜け出す道になるかも。アイデアの要約だけど、JITの人たちがどのPythonの特徴が厄介だと思っているのか聞くのは、確かに興味深い第一歩だね。

__del__について言及するのは面白いね。JavaScriptにはデストラクタがないだけじゃなくて、セキュリティ上の理由(僕の給料では理解できないけど)で、仕様がガベージコレクションの状態を見えるようにする実装を明示的に禁止してるんだ。つまり、コードは解放に関する情報を得ることができないってこと。__del__は難しいと思うけどね。理論的には__del__は信頼できるものじゃないはずなんだ。実際にはCPythonがちゃんと呼び出すから、参照カウントしてるし。だからみんなそれを知って使ってる(ただ、実際には最善を尽くすクリーンアップチェックに使われることが多いけど)。もしもっと多くの人がPyPyを使っていたら、その視点から避けるようなプレッシャーがあったかもしれないし、それが「どんな」システムでもパフォーマンスの良いコードを実装するためのプレッシャーにもなるだろうね。

一番大きな問題は、デフォルトでBigIntがあることだね。これがあると、すべての整数操作でオーバーフローチェックが必要になるんだ。

[遅延]