Python 3.14で実現した“真の”マルチスレッド

Python 3.14で実現した“真の”マルチスレッド

2025年7月にリリース予定の Python 3.14 Beta 3 より、いよいよ GILを無効化できる「無GIL(Free‑threaded)」ビルド が正式にサポートされました。これは、2023年にPEP 703が採択された後、Phase II(正式サポート)に移行した証しです  。

目次

💡 背景:なぜGILが問題だったのか?

Pythonの標準実装であるCPythonには、GIL(Global Interpreter Lock:グローバルインタプリタロック)という仕組みが長年存在してきました。このGILは、同時に複数のスレッドがPythonオブジェクトを操作しないようにする排他制御のためのロック機構です。

一見するとGILは、Pythonを簡潔で安全に保つための仕組みに見えます。たしかに、それは事実でもあります。たとえば、PythonはC言語で書かれており、その内部ではメモリ管理に「参照カウント方式」を使っています。あるオブジェクトが何回参照されているかを記録しておき、そのカウントが0になったときにオブジェクトを破棄するという方式です。

ところが、この参照カウントを複数のスレッドから同時に変更しようとすると、レースコンディション(同時書き込みによる矛盾)が発生します。これを防ぐために導入されたのがGILであり、「1度に1つのスレッドしかPythonのバイトコードを実行できない」という制約を生み出しています。

🔧 GILの本質的な問題点

このようにGILはPythonの内部実装を簡潔に保つうえで大きな役割を果たしていましたが、時代が進むにつれて致命的なボトルネックとなりました。特に次のような点で深刻な影響がありました:

1. マルチコアCPUを活かせない

今日のCPUは複数のコアを持ち、並列処理による高速化が当たり前になっています。しかし、GILの存在により、複数スレッドを起動しても、同時に実行できるのは1つだけです。これではせっかくのマルチコアの性能を十分に活用できません。

2. スレッド並列の性能が出ない

たとえば、Pythonで画像処理や科学技術計算などのCPUバウンドな処理を複数スレッドで並列化しても、GILがあると結局は「順番に1つずつ」処理されるため、実行速度がほとんど改善しないのです。

3. 開発者の混乱と回避策の複雑化

Pythonでは「threading モジュールで並列処理できる」と言いつつも、実際には真の並列性を得るには multiprocessing モジュールを使ってプロセス並列にする必要があるという“隠れた制約”がありました。プロセス並列はオーバーヘッドが大きく、共有メモリやキューの扱いも複雑で、初心者には難解です。

4. 高速化できない → 他言語への逃避

AIやWebサーバー、並列クローラなど高負荷な処理を伴う分野では、「Pythonは便利だけど遅い」と言われ、性能が必要な部分だけをC++やRustで書くハイブリッド構成が一般的になりました。これは開発コストや保守性の面で課題が大きいアプローチです。

🌀 GILのパラドックス:安全と性能のトレードオフ

GILは一種の「安全装置」です。PythonのオブジェクトモデルやGCをスレッドセーフに保ち、ライブラリ開発者が内部実装のロック処理をそれほど意識しなくて済むようにしたという点では、**Pythonらしい“実用主義の設計”**とも言えます。

しかし、スレッド安全の代償として性能を犠牲にしていたことが、マルチコア化が進んだ現代のシステムでは大きな足かせとなっていました。

💥 AI・データ処理時代の限界

特に近年は、ディープラーニングの学習やデータ前処理のような高負荷な処理をスケーラブルに行うニーズが急増しています。こうした場面でGILの制約は致命的であり、Pythonの採用を避ける、または他言語に置き換えるといった選択が現実に増えてきていました

🚪 だからこそ「GILの撤廃」は長年の悲願だった

「Pythonの使いやすさと、C/C++並みの並列性能を両立させたい」

その願いに応えるべく登場したのが、無GIL(no-GIL)ビルドのPythonであり、PEP 703がそれを実現する第一歩となったのです。

🚀 PEP 703の採択とPhase 移行

🧾 PEP 703とは何か?

PEP 703(正式名称:Making the Global Interpreter Lock Optional in CPython)は、CPythonからGIL(Global Interpreter Lock)を取り除くことを可能にする提案です。著者はMeta(旧Facebook)所属のエンジニアである Sam Gross 氏。彼は長年にわたって「nogil-python」という独自フォークで、GILを排除したPython実装を開発・検証してきました。

このPEPは、「すべてのPython実装を無GILにしよう」というものではなく、「GILあり/なしをビルド時に選べるようにする」という、現実的で段階的な方針をとったことが評価されました。

📅 採択までの流れ

  • 2021年〜2022年:Sam Gross氏が独自に nogil ビルドを開発。実際のコードベースで性能・安定性を実証。
  • 2023年4月:PEP 703 が公式に提出される。予想以上の注目を集め、Python開発者コミュニティで大規模な議論が展開される。
  • 2023年7月:Pythonの意思決定機関である Steering Council(SC) が、PEP 703を条件付きで採択

Steering Councilによる採択の主旨は、「GILなしビルドはPythonの将来にとって重要なオプションになる」という判断に基づくものです。採択にあたり、以下のような条件が提示されました:

  • ABI(バイナリ互換性)やツールチェインの影響評価
  • 標準ライブラリや主要外部ライブラリとの互換性
  • パフォーマンスへの影響が限定的であることの証明
  • 開発体制(コミットメント)の持続性

🚨 採択時の公式声明:https://discuss.python.org/t/a-steering-council-notice-about-pep-703-making-the-global-interpreter-lock-optional-in-cpython/30474

📦 Phase制とは?

PEP 703の実現には段階的な導入が不可欠です。そこで提案されたのが **「3フェーズ構成」**です:

フェーズ内容状態(2025年7月現在)
Phase I実験的導入(experimental):一部の開発者・パワーユーザーが手動でビルドして評価する段階✅ 完了(3.13)
Phase II正式サポート(supported):公式ビルドに –disable-gil オプションを導入し、開発者が広く使用可能に✅ 現在進行中(3.14 Beta 3〜)
Phase IIIデフォルト化(default):将来的にGILなしPythonを標準ビルドとすることを目指す🔜 将来検討

📍 Phase II:Python 3.14で何が変わる?

2025年7月現在、Python 3.14 Beta 3 にて –disable-gil オプションによる 「無GILビルド」が正式に同梱 されました。これにより、以下が可能になります:

  • 通常のCPythonソースコードから、./configure –disable-gil で free-threaded Python(無GIL)を構築可能
  • サードパーティライブラリ開発者は、本格的に互換性やパフォーマンスの検証に取り組める
  • 今後、PyPIのメタデータにもGIL対応情報が加わる予定

これは開発者にとって非常に大きな転機であり、「GILなしの未来に向けた準備を本格化する段階」と言えます。

💬 採択における議論のポイント

PEP 703の採択には、以下のような懸念と議論が活発に交わされました:

  1. 性能劣化への不安:GILを取り除くことで単一スレッド性能が落ちるのでは?
  2. メモリ使用量の増加:オブジェクトごとのロック管理により、メモリが増加しないか?
  3. 既存の拡張ライブラリとの互換性:PyTorch、NumPyなどが対応できるのか?
  4. メンテナンスコスト:2種類のビルド(GILあり/なし)を保守し続けられるのか?

これらに対してSam Gross氏は、実装レベルでの性能改善策(アトミック参照カウント、バイアス付きRCなど)や、十分なバックワード互換性の維持、長期的な支援体制を提案し、結果として採択に至りました。

🔮 Phase IIIへ向けて:今後の課題と期待

Phase IIの間に、次のステップに向けた評価と実績の蓄積が求められます:

  • 実用例の拡大(Flask/FastAPI/Django等でのベンチマーク)
  • PyPIパッケージの対応率向上
  • CPythonチームとコミュニティによる安定性テスト
  • エコシステムのドキュメント整備と開発者教育

こうした課題をクリアした上で、将来的に Phase III(GILなしが標準) へと進むことが期待されています。これはPythonの設計思想を大きく刷新する歴史的な転換点となるでしょう。

🧠 CPython内部での変化

Python 3.14で「GILなしビルド(Free-threaded Build)」が正式に導入されるにあたり、CPythonの内部には抜本的な再設計が施されました。単にGILを「オフにする」だけではなく、GILが前提だった数多くの内部機構を、並列スレッドに耐える構造へと刷新する必要があったのです。

以下に、主な内部変更点とその意図、技術的背景を詳しく紹介します。

🔁 1. アトミックな参照カウントへの移行

CPythonでは、メモリ管理の中心に「参照カウント(reference counting)」を用いています。オブジェクトが何回使われているかをカウントし、0になった時点でメモリを解放するという仕組みです。

従来、GILがあることで参照カウントのインクリメント・デクリメントは排他制御なしでも安全に行えました。しかしGILを除去する場合、複数スレッドが同時に参照カウントを操作するため、**アトミック操作(atomic operations)**が必要になります。

  • Python 3.14では、GCC/Clangなどのコンパイラが提供する __atomic_fetch_add() や std::atomic を活用し、スレッド安全な参照カウントを実現。
  • ARMやx86などのプラットフォームに対応するため、クロスプラットフォームなアトミック処理の実装が行われました。

この改良により、オブジェクトのライフサイクルがマルチスレッド下でも一貫して安全に動作するようになります。

⚖️ 2. バイアス付き参照カウント(Biased Reference Counting)

ただアトミックにするだけではオーバーヘッドが大きいため、Python 3.14では 「バイアス付き参照カウント(biased refcount)」 という最適化も導入されました。

  • 各スレッドに スレッドローカルの参照カウントキャッシュを持たせることで、頻繁なグローバル競合を回避。
  • 一定条件でしかグローバルカウントに反映しないことで、ロックやアトミック操作の頻度を減らす工夫。

これにより、GILありのPythonに近い性能を維持しながら、GILを撤廃するという難題に現実的な解決策が与えられました。

🧹 3. ガーベジコレクタ(GC)の見直し

PythonのGC(ガーベジコレクタ)は、参照カウントとは別に 循環参照 を検出してオブジェクトを解放する機構です。

GILがある場合は、GCの途中で他のスレッドがオブジェクトを触ることがなかったため、非常に単純でした。しかしGILなし環境では:

  • GC中にも他スレッドからオブジェクトが変更される可能性がある。
  • 複数スレッドが同時にGCを走らせると、同じメモリに対して並列にアクセスしてクラッシュする恐れがある。

これを防ぐために、Python 3.14の無GILビルドでは:

  • Stop-the-world型GC を採用:GCを行うときは全スレッドを一時停止。
  • 同期バリア の導入:GC開始時にスレッド間で同期を取り、整合性を確保。

こうした工夫により、従来のGCを可能な限り流用しながら、スレッドセーフな循環検出とメモリ解放を実現しています。

🧱 4. スレッドローカル・ヒープとオブジェクトアロケータ

さらに、メモリの割り当てと解放のスレッド競合を回避するために、スレッドごとのヒープ領域(Thread-Local Heaps) が導入されました。

  • 各スレッドが独立したアロケータを持ち、小さなオブジェクトの割り当てを効率化。
  • 共有アロケータへのアクセス頻度を下げることで、ロックの衝突を回避し性能を向上

また、一部のオブジェクト(文字列やタプルなど)については「不変性(immutability)」を前提にした読み取り中心の最適化が行われ、スレッド間での共有にも強くなりました。

🔓 5. オブジェクトごとのロック(Per-object Locking)

一部のオブジェクト(例:辞書、リストなど)は、内部状態を変更する操作が並列実行されると整合性が崩れる可能性があります。

そこで、Python 3.14ではこうしたオブジェクトに 内部ロック(mutex) を導入し、以下を制御:

  • dict.__setitem__() や list.append() のような変更系操作でロックを取得
  • 読み取りはロックフリーで許容(必要に応じて)

これにより、開発者がユーザーレベルで複雑なロックを記述せずとも、CPython内部で整合性が担保されるようになりました。

🧬 6. ABIの互換性と2モードビルド体制

GILあり・なしのビルドを共存させるため、ABI(Application Binary Interface)レベルでの分離も検討されています。

  • PyPIに「no-gil互換」のフラグを導入
  • C拡張モジュールがGILあり/なしどちらに対応しているかを明示
  • ビルド時に –disable-gil を指定すれば、別モードのPythonがインストールされる

この柔軟な構成により、段階的な移行と後方互換性の維持が可能になります。

🔚 まとめ

変更点内容狙い
参照カウントアトミック+バイアス方式並列更新の安全確保と高速化
GCStop-the-world型・同期バリア導入循環参照検出のスレッド安全性
メモリアロケータスレッドローカルヒープメモリ競合の削減
オブジェクト保護内部ロック・不変性活用共有オブジェクトの整合性維持
ABI構造GILあり/なしのビルド分離ライブラリ互換性と移行支援

このように、Python 3.14の無GIL対応は、単なる「ロック解除」ではなく、CPythonのメモリ管理、オブジェクトモデル、実行モデルを抜本的に見直す再設計の結晶なのです。

🧪 パフォーマンスとメモリ傾向

GILを無効化することで「マルチスレッドが速くなる」のは直感的に理解しやすいですが、実際にどの程度の性能向上があるのか、また逆にどんな副作用があるのかは、多くのPythonユーザーが気になるところでしょう。

このセクションでは、Python 3.14無GILビルドにおける実測ベンチマークやメモリ使用傾向、そしてそこから見える実運用上の注意点を詳しく見ていきます。

🏁 性能評価の方法:pyperformanceベンチマーク

性能比較には、Python公式のベンチマークスイート pyperformance が使われています。これはPythonの代表的な処理(数値演算、テキスト処理、正規表現、圧縮・展開など)を測定するためのツールです。

PEP 703 の開発者である Sam Gross 氏や、他の開発者・研究者によって実際に行われた比較では、以下のような傾向が明らかになりました。

📊 ベンチマーク結果(概要)

🔹 単一スレッド性能

テスト対象通常のCPython(GILあり)無GILビルド性能差(参考)
正規表現マッチ1.00 倍0.94 倍−6% 程度
JSONエンコード1.00 倍0.93 倍−7% 程度
数値演算(浮動小数)1.00 倍0.89 倍−11% 程度
データ構造操作1.00 倍0.96 倍−4% 程度

➡️ 平均して約5〜10%の性能低下が観測されます。これはアトミック参照カウントや内部ロックによるオーバーヘッドが原因です。

🔹 マルチスレッド性能(4スレッド以上)

テスト対象GILあり無GIL向上率
数値演算 × 4スレッド約3.8秒約1.1秒約3.5倍高速化
gzip圧縮約3.2秒約1.2秒約2.7倍高速化
並列Web API処理約1100 req/s約3100 req/s約2.8倍高速化

➡️ マルチコア環境下では劇的な性能改善が見られます。これこそが無GILビルドの最大のメリットです。

🔍 CPUバウンド vs I/Oバウンド

タイプ無GILの影響
CPUバウンド処理✅ 大きな改善。計算系・画像処理・暗号化などに強い。
I/Oバウンド処理⭕ 少し改善。スレッド間切り替えが減り安定するがASGI/asyncには及ばない。

とくに NumPy, Pillow, PyTorch のような計算系ライブラリとの組み合わせでは、スレッドワークのスケーリングが現実的になり、実行時間を大幅に短縮できるケースが多くなっています。

📈 メモリ使用量の傾向

無GIL化に伴い、CPython内部では以下のような理由からメモリ使用量が増加する傾向があります:

  • 参照カウントに追加のメタ情報(バイアス構造体など)が必要
  • オブジェクトごとの内部ロック/バリア/スレッド局所ヒープの導入
  • GCの一時的データ構造の増加

その結果、Sam Gross氏のベンチマークでは:

  • 全体メモリ使用量が約15〜20%増加(ワークロードによる)
  • スレッド数が増えるほどヒープや同期コストが上乗せされやすい

📌 実運用でのインパクトは?

処理タイプパフォーマンス影響メモリ影響備考
シングルスレッドのWeb APIわずかに遅くなる(5〜10%)微増性能劣化が許容されるなら問題なし
並列スクレイピング最大3倍高速化やや増加リクエストを多数処理する用途に最適
バッチ処理・科学計算数倍速くなるやや増加ThreadPoolExecutorを積極活用できる

💬 開発者の声・評価

  • 「並列処理の効果がコード量を減らすことで顕著に現れる」
  • 「multiprocessingを使わずに済むだけで、コードの可読性とデバッグ性が格段に向上」
  • 「NumPyやPyTorchが対応してくれれば、Pythonの並列処理が現実解になる」

といった前向きな意見が多く、Pythonでスケーラブルなアプリを書くことへの期待が高まっています

✅ まとめ

  • 単一スレッド性能は最大で10%程度の低下があるが、ほとんどのケースでは実用的。
  • マルチスレッド処理では2〜4倍以上の大幅な高速化が実現可能。
  • メモリ使用量は平均して15〜20%程度の増加が見られるが、トレードオフとしては妥当。
  • GILなしで「Pythonらしいコード」のまま並列性を活かせることは、開発者にとって大きなメリット。

✅ まとめ

2025年7月にリリースされた Python 3.14 Beta 3 において、ついに 「無GIL(Global Interpreter Lock)ビルド」 が正式にサポートされる段階、すなわち Phase II(正式サポートフェーズ) に入りました。これにより、開発者は –disable-gil オプションを使用して、GILのないビルドを手軽に試すことができるようになり、Pythonにおける並列処理の可能性が大きく広がる節目を迎えたと言えます。

この無GILビルドでは、単体スレッドの性能においてはおおよそ5〜10%程度のパフォーマンス低下が見られるものの、これはアトミック参照カウントや同期処理によるわずかなオーバーヘッドによるものであり、通常のアプリケーションにおいて致命的な影響を及ぼすレベルではありません。一方で、マルチスレッド処理においてはGILの制約が完全に取り払われたことにより、複数スレッドによる“真の並列実行”が可能となり、実行速度が2倍〜4倍、場合によってはそれ以上に向上するケースも報告されています。

もちろん、GILが取り除かれたことによって内部構造も大きく変わっており、拡張モジュール(C拡張など)との互換性や、メモリ使用量の増加、スレッドセーフなコード設計の重要性など、開発者が注意すべき点も少なくありません。とくに、PyPI上に存在する数多くのサードパーティライブラリが無GIL環境に対応するには一定の時間と検証が必要であり、慎重な移行計画が求められる状況です。

とはいえ、Python本体とエコシステム全体がこの変化に向けて大きく動き始めているのは確かであり、今後1〜2年のうちに、主要なライブラリの対応やパッケージングの整備が進めば、「GILなし」がPythonの新たな標準となる日も決して遠くはないでしょう。本格的な移行に向けた「助走期間」として、まさに今が絶好のタイミングだと言えます。

今回のリリースは、30年以上続いてきたPythonの実行モデルに対する最大級の刷新であり、並列性の課題に対してついに本格的な解決策が提供されたという点で、歴史的な意義を持っています。これからのPythonは、より強力に、そしてよりスケーラブルに進化していくことでしょう。

📚 参考文献

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次