SJ blog
backend
A

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

Python 3.14がGILを廃止——フリースレッドで真の並列処理がついに実現

Python 3.14.4でフリースレッド(GIL廃止)が正式サポート。CPUバウンドタスクで最大4倍の速度向上を得る仕組みと使い方、スレッド安全性の落とし穴を解説。

一言結論

Python 3.14でGILが廃止され真の並列処理が可能になったが、シングルスレッドで5〜10%の性能低下があり既存コードのスレッド安全性を再確認する必要がある。CPUバウンドタスクに絞って活用するのが現実的な戦略だ。

なぜ GIL 廃止が「歴史的」なのか

GIL(Global Interpreter Lock) は 1991 年の CPython 実装以来、Pythonに存在し続けてきたグローバルな排他ロックだ。その役割はシンプルで、「一度に実行できる Pythonバイトコードのスレッドを1つに制限する」というものだった。

GIL の存在理由:
  CPython のオブジェクトは参照カウント(refcount)で管理される。
  複数スレッドが同じオブジェクトに同時に触れると refcount の
  破損やメモリ二重解放が起きる。GIL はその保護膜だった。

  代償:
    - スレッドを100個作っても、CPU コアをフルに使えない
    - マルチコア CPU の恩恵を受けるには multiprocessing が必要
      → プロセス起動コスト・メモリ重複というオーバーヘッド

PEP 703「Making the GIL Optional」が採択され、Python 3.13 で実験的サポートが始まり、Python 3.14(2026年4月7日リリースの 3.14.4 で正式サポート、PEP 779) でついにフリースレッドが安定機能として昇格した。

フリースレッド Python の仕組み

参照カウントをどう守るか

GIL を取り除いた CPython は、オブジェクトのスレッド安全性を次の2方向で担保している。

1. Biased Reference Counting(偏向参照カウント)
   → オブジェクトの「所有者スレッド」がローカルカウンタを持ち、
     他スレッドからのアクセスのみアトミック操作を使う。
     ほぼすべての操作がローカルで完結するため競合が最小化される。

2. Object-level ロック(ビルトイン型)
   dict / list / set などの組み込み型は内部ロックを持ち、
   インタープリタがクラッシュしないことを保証する。
   ただし論理的な競合(読み書きの順序)は保護しない。

インストール方法

# Python.org 公式インストーラ(Windows)
# python3.14.exe  → GIL あり(標準)
# python3.14t.exe → GIL なし(t = free-Threaded)

# Linux/macOS ソースビルド
./configure --disable-gil
make -j$(nproc)

# pyenv を使う場合(3.14.4+)
PYTHON_CONFIGURE_OPTS="--disable-gil" pyenv install 3.14.4t
pyenv local 3.14.4t

# 確認
python3.14t -c "import sys; print(sys._is_gil_enabled())"
# → False

パフォーマンス:何が速くなり、何が遅くなるか

CPU バウンドタスクで最大 4 倍

import threading
import time

def sum_squares(n: int) -> int:
    return sum(i * i for i in range(n))

N = 20_000_000
THREADS = 4

# GIL あり Python 3.12:
# 4スレッドで N を分割しても実質シリアル実行
# 合計時間 ≈ 1スレッドと変わらない

# GIL なし Python 3.14t:
start = time.perf_counter()
threads = [
    threading.Thread(target=sum_squares, args=(N // THREADS,))
    for _ in range(THREADS)
]
for t in threads:
    t.start()
for t in threads:
    t.join()
elapsed = time.perf_counter() - start

# 4コアマシンで ≈ 4倍高速化が得られる
print(f"Elapsed: {elapsed:.2f}s")

シングルスレッドは 5〜10% 遅くなる

ベンチマーク(pyperformance スイート):
  python3.14   (GIL あり): ベースライン 100%
  python3.14t  (GIL なし): 約 90〜95% ← 5〜10% 低下

  理由:
    - Biased Refcount のオーバーヘッド
    - ビルトイン型の内部ロック取得コスト
    - コンパイラ最適化がアトミック操作で制限される

I/O バウンドタスク(Web API 呼び出し、DB クエリ等)では、もともと asynciothreading でイベントループが処理するため、フリースレッドにしてもほぼ変化なし。

実際のユースケース

✅ フリースレッドが効果的なケース

# 画像処理(NumPy / PIL)
import numpy as np
import threading

def process_chunk(arr: np.ndarray) -> None:
    # CPU ヘビーな変換処理
    arr[:] = np.sqrt(arr) * np.log1p(arr)

data = np.random.rand(4, 10_000_000)
threads = [threading.Thread(target=process_chunk, args=(data[i],)) for i in range(4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
# GIL なしなら 4 コアを使い切れる

# 他の適用例:
# - 機械学習の特徴量エンジニアリング(CPU 集約)
# - 暗号化・ハッシュ計算の並列処理
# - シミュレーション・モンテカルロ法
# - 動画/音声のトランスコード前処理

❌ フリースレッドで注意が必要なケース

import threading

# 共有状態への書き込み → 明示的ロックが必要
counter = 0
lock = threading.Lock()

def increment(n: int) -> None:
    global counter
    for _ in range(n):
        # ❌ ロックなし: GIL がなくなったので競合が起きる
        # counter += 1

        # ✅ 明示的ロック必須
        with lock:
            counter += 1

threads = [threading.Thread(target=increment, args=(100_000,)) for _ in range(4)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(counter)  # ✅ 400000 が保証される

GIL 時代は「counter += 1 の競合はまれで、クラッシュしないことが多かった」が、フリースレッドでは共有 mutable 状態は必ずロックで守らなければならない

ライブラリの互換性

2026年4月時点の主要ライブラリ対応状況(フリースレッドビルド):

  ✅ 対応済み(公式サポート):
    NumPy 2.2+       → GIL 解放対応
    SciPy 1.14+      → フリースレッドテスト通過
    Pillow 11.0+     → スレッドセーフに書き直し済み

  ⚠️ 部分対応 / 要確認:
    pandas 2.3       → コアは対応、一部 API で警告あり
    SQLAlchemy 2.0+  → セッション単位のスレッド安全性に注意

  ❌ 未対応 / 動作不定:
    C 拡張モジュールで GIL を前提とした実装は再コンパイル必要
    → py-free-threading.github.io でライブラリ別互換性リストを確認

移行戦略

ステップ1: まず現行コードを Python 3.14(GIL あり)で動かす
           → バグが GIL 廃止由来かどうかを切り分け

ステップ2: threading.py の PYTHONGIL=0 環境変数でテスト
           export PYTHONGIL=0
           python3.14 your_script.py
           → フリースレッドに近い挙動を確認できる

ステップ3: python3.14t に切り替えてテストスイートを実行
           → 競合状態を ThreadSanitizer やレースコンディション
             テスト(threading.settrace)で検出

ステップ4: CPUバウンドの処理を threading に移行
           → multiprocessing から threading への置き換えで
             メモリ共有・起動コスト削減の恩恵を得る

注意点・未確認情報

  • 「最大4倍の速度向上」はコア数・タスクの種類に依存する。すべてのケースで4倍になるわけではない
  • C 拡張モジュールはフリースレッドで再コンパイルしないと旧来の GIL 前提コードが実行されることがある(GIL が自動的に再有効化される)
  • PyPy・GraalPy など代替実装への影響は別途確認が必要

まとめ

Python 3.14 のフリースレッドは、30年以上続いた GIL の呪縛からの解放だ。ただし「すべてのコードが自動的に速くなる」わけではない。効果が出るのは CPUバウンドかつ並列化可能なタスクに限られ、その恩恵を得るには明示的なスレッド安全性の確保が求められる。まずは影響が局所的なバッチ処理や特徴量エンジニアリングから試すのが現実的な第一歩だ。

参考リンク