Python 3.14でfree-threadingビルドが正式サポートになりました。GILが外れたと聞くと「asyncioを書かずにスレッドだけで速くなる」と読みたくなります。ただ効くのは純PythonのCPUバウンドな並列処理に限られ、IO待ちの並行処理は今もasyncioの担当です。
free-threadingが「正式サポート」になった意味
Python 3.13までfree-threadingは実験扱いでした。3.14(3.14.0、2025年10月リリース)で位置づけが変わります。
experimentalが外れたPEP 779
「What’s New in Python 3.14」のハイライトに “PEP 779: Free-threaded Python is officially supported” が並びました。公式の言い回しは “officially supported but still optional”。デフォルトのビルドは今もGIL有効で、free-threadingは選んで使う phase II の段階です。実験フラグは外れた、ただし標準ではない。インストール時に明示して選ばないと、free-threadingビルドは手元に入りません。
python3.14t という別バイナリ
free-threadingは通常のpython3.14とは別のバイナリとして配布されます。末尾にtが付いたpython3.14tがそれ。同じ3.14でもGIL有効ビルドと無効ビルドは別物で、共存できます。「3.14にしたらGILが消えた」わけではない点に注意してください。
手元のPythonがfree-threadedか確かめる
公式howtoの “Identifying free-threaded Python” セクションが、判定方法を2つ挙げています。
sys._is_gil_enabled() と Py_GIL_DISABLED
実行時の状態とビルドの素性は別物。混同すると、GILが戻った状態を「free-threading非対応」と誤読します。
import sys
import sysconfig
# 実行時にGILが動いているか(再有効化されていないか)
print(sys._is_gil_enabled())
# このビルドがfree-threadingに対応しているか(推奨される判定)
print(sysconfig.get_config_var("Py_GIL_DISABLED"))
python3.14tで動かした結果です。
False
1
sys._is_gil_enabled()は実行時の状態、Py_GIL_DISABLEDはビルドの素性を表します。公式はビルド判定には後者を推奨。前者は後述のGIL再有効化で値が変わるためです。
uvで入れるのが速い
入手経路は3つ。公式インストーラ(macOS/Windowsは追加コンポーネントのチェックで導入)、ソースからの./configure --disable-gil、そしてuvです。uvなら1行で済みます。
uv python install 3.14t
uv run --python 3.14t python -c "import sys; print(sys._is_gil_enabled())"
False
CPUバウンドな処理を並列で実測する
通常のCPythonは、純Pythonのループを複数スレッドへ分けてもGILが1つずつしか実行させません。GILは1個きりのロックで、どのスレッドもPythonバイトコードを動かす前に必ず取得する。だから4スレッド起動しても、進むのは実質1コアぶん。free-threadedビルドはこのロックを撤廃し、スレッドが別々のコアで同時にバイトコードを実行できるようにします。
同じコードをGIL有効・無効で回す
純Pythonの数値ループを、スレッド数を変えて回すベンチを用意しました。NumPyのようなCレベルでGILを解放する処理ではなく、わざとPythonの実行で詰まらせる形にしています。
import sys
import time
from concurrent.futures import ThreadPoolExecutor
def cpu_task(n: int) -> int:
total = 0
for i in range(n):
total += i * i % 7
return total
def run(workers: int, total_iters: int) -> float:
chunk = total_iters // workers
start = time.perf_counter()
with ThreadPoolExecutor(max_workers=workers) as ex:
list(ex.map(cpu_task, [chunk] * workers))
return time.perf_counter() - start
if __name__ == "__main__":
print("GIL:", sys._is_gil_enabled())
iters = 200_000_000
for w in (1, 4, 8):
print(f"{w} threads: {run(w, iters):.2f}s")
同じスクリプトをpython3.14(GIL有効)とpython3.14t(GIL無効)で実行した結果です。
$ python3.14 bench.py
GIL: True
1 threads: 3.80s
4 threads: 3.90s
8 threads: 4.01s
スレッドを増やしたときの伸び
8コアのLinux(x86-64、Python 3.14.0)で測った値を並べます。GIL有効ビルドはスレッドを増やしても頭打ち、free-threadedはコア数なりに伸びました。
| スレッド数 | python3.14(GIL有効) | python3.14t(GIL無効) |
|---|---|---|
| 1 | 3.80s | 4.12s |
| 4 | 3.90s | 1.31s |
| 8 | 4.01s | 0.92s |
GIL有効ビルドは4スレッドでも3.90sで、1スレッドの3.80sからほぼ動きません。free-threadedは8スレッドで0.92s、1スレッド比で約4.5倍。8コアぶんがそのまま効いています。公式howtoの言葉を借りれば、”Free-threaded execution allows for full utilization of the available processing power by running threads in parallel on available CPU cores” がそのまま数字に出た形です。ただし純Pythonのループ限定。NumPyやPolarsのように内部でGILを解放するライブラリは、GIL有効ビルドでもとっくに並列で動いています。
multiprocessingと何が違うか
この並列化、これまではmultiprocessingで別プロセスを立てて回避してきました。ただプロセス間はメモリを共有しないので、渡すデータはpickleで直列化され、ワーカー起動のコストもかかります。free-threadingは同一プロセス内のスレッドなので、巨大な共有データを各スレッドが直接読めます。数GBのモデルや辞書をワーカーごとに複製していたなら、ここが効く。pickleにできないオブジェクトをスレッドへ渡せる点も差です。
free-threadingが効かない処理
速くなるのはCPUを使い切る処理だけ。検索サジェストに「python 3.14 gil fastapi」が出るあたり、Web APIの高速化を期待する向きがありますが、ここは見込み違いになりがちです。
IO待ちはasyncioのまま
HTTPリクエストやDBクエリの待ち時間は、CPUをほとんど使いません。レスポンスが返るまでスレッドが寝ているだけ。この待ち時間はGILの有無と無関係で、asyncioのイベントループが1スレッドで何百本もの接続を捌きます。FastAPIのIOバウンドなエンドポイントをfree-threadingへ置き換える理由はありません。
シングルスレッドは5〜10%遅くなる
GILを外した代償もあります。公式howtoの “Single-threaded performance” は “The performance penalty on single-threaded code in free-threaded mode is now roughly 5-10%, depending on the platform and C compiler used” と書いています。3.13より縮みましたが0ではない。先のベンチでも1スレッドが3.80s→4.12sと約8%遅くなっていました。並列で取り返せる処理かどうかが分かれ目です。
C拡張ライブラリとcp314tホイールの現状
free-threaded buildで困るのはたいてい依存ライブラリ側です。C拡張がGILなしで安全に動くと宣言していないと、恩恵を受けられません。
cp314t タグのホイール
free-threaded build向けのホイールはcp314tというABIタグを持ちます。GIL有効版のcp314とは別枠。python3.14t上ではpipもuvも自動でcp314tホイールを選びます。2026年5月時点の対応状況をざっと並べます。
| ライブラリ | cp314t ホイール | 補足 |
|---|---|---|
| NumPy | 配布あり | 2.1系から対応。並列計算の土台 |
| Cython | 対応 | モジュール側で対応宣言が要る |
| pandas | 配布あり | 内部はNumPy依存 |
| 対応待ちの拡張 | 未配布あり | importでGILが自動再有効化 |
未対応の拡張を読むとGILが戻る
free-threading対応を宣言していないC拡張をimportすると、CPythonは安全側に倒してGILを自動で再有効化し、警告を出します。せっかくpython3.14tで起動しても、依存に1つ未対応拡張が混ざると並列化が消える。明示的にGILを戻すなら環境変数かコマンドラインオプションです。
PYTHON_GIL=1 python3.14t app.py
# または
python3.14t -X gil=1 app.py
>>> import sys
>>> sys._is_gil_enabled()
True
この挙動があるので、ビルド素性の判定にはPy_GIL_DISABLED(変わらない)、実行時の実態確認にはsys._is_gil_enabled()(戻ると変わる)を使い分けます。依存をuv pip installした直後にこの2行で実態を確認しておくと安全です。cp314tホイールの無いライブラリは、ソースビルドにフォールバックするか、警告とともにGILを戻すか、どちらかになります。
本番に入れるかの判断
投入価値があるのは、純PythonのCPU処理がボトルネックで、かつ依存C拡張がcp314t対応済みのケース。画像の前処理、独自パーサ、シミュレーションのような計算をスレッドで分けられるなら、8コアで3〜4倍は現実的な伸びです。
逆にIOバウンド中心のWeb APIやバッチは、シングルスレッド5〜10%の劣化だけ受けて並列の恩恵が薄い。既存のIOバウンドなFastAPIアプリをpython3.14tへ載せ替えて速くしようとする使い方は外れます。スレッドが待つ時間はGILの有無で変わらないからです。一方、画像のリサイズを数百枚まとめてかけるバッチをThreadPoolExecutorで分けるなら、8コアで素直に縮みます。まず自分のワークロードを先のベンチ形式で測り、スレッドを増やして時間が縮むかを確かめてから決めてください。phase IIが「optional」のままなのは、こうした測定とライブラリ確認を各自でやる前提だからです。
まとめ
- Python 3.14でfree-threadingが正式サポート(PEP 779)。別バイナリ
python3.14tとして配布され、デフォルトは今もGIL有効 - 効くのは純PythonのCPUバウンド並列。8コア実測で1スレッド比4.5倍、4スレッドでも約3倍
- IO待ちは引き続きasyncioの担当。FastAPIのIO処理をfree-threadingで置き換えても速くならない
- シングルスレッドは5〜10%のオーバーヘッド。並列で取り返せるかが投入の分かれ目
- C拡張は
cp314tホイール対応が前提。未対応をimportするとGILが自動で戻り、並列化が無効になる

