Python 3.14フリースレッド実装|GIL無効化でマルチスレッドが本当に動くようになった

プログラミング

Pythonのマルチスレッドが「ついに本当に動くようになった」という話、なんとなく聞いてはいたけどちゃんと調べたことがなくて、ようやく腰を上げました。

この記事でわかること:

  • GIL(Global Interpreter Lock)とは何か、なぜ問題だったか
  • Python 3.14のフリースレッドモードで何が変わったか
  • 実際に試す方法(uvを使ったインストール)
  • 使うときの注意点・現時点での制限

GILとは何だったのか、おさらい

GIL(Global Interpreter Lock)とは、複数のスレッドが同時にPythonコードを実行することを防ぐためのものです。一言でいうと「同時にPythonコードを実行できるスレッドは1つだけ」という制約。

Pythonではスレッドが利用できますが、GILの制限により複数CPUコアを使っても動くコアは1つだけで、真の並列処理はできませんでした。複数のCPUコアを活用するにはmultiprocessingを使ってプロセスを複数立ち上げるしかありませんでした。

じゃあなぜそんな仕組みが存在していたかというと、理由はシンプルで「参照カウントベースのメモリ管理をスレッドセーフにするため」です。GILありのPythonではシンプルな形でスレッドセーフを実現していたが、フリースレッド版PythonではGILなしでスレッドセーフを実現しながら、複数のスレッドが複数のCPUコアを活用できるようにするために、GILありのPythonと比べてコスト(速度低下やメモリ使用量の増加)が発生します。

余談ですが、I/O待ちが多いWebサーバーなどの用途では、GILがあっても割とうまく動いていたんですよね。問題が顕在化するのは主にCPU-boundな並列処理のとき。

Python 3.13と3.14でどう変わったか

GILをなんとかしようという動きは長年あって、PEP 703として提案されている流れがあります。ロードマップについてもいろいろ議論されているようで、ここでは大まかに整理すると、

  • フェーズ1(Python 3.13):実験的サポート。python3.13tという別バイナリとしてビルド可能になった
  • フェーズ2(Python 3.14):サポートが進みつつある。ただしデフォルトは依然GIL有効
  • フェーズ3(Python 3.15以降?):フリースレッドモードがデフォルトになるかどうかは、現時点では未定のようです

Python 3.14は2025年10月にリリースされました。Python 3.13から「free threading(GIL無効化ビルド)」自体はサポートされていて、3.14でも引き続きfree-threadedビルドが使えます。

3.14における改善点として注目したいのが、シングルスレッド時のパフォーマンスペナルティの改善です。公式ドキュメントの説明だと、pyperformanceベンチマークにおける平均オーバーヘッドは環境にもよりますが、1~8%程度とされています。これは地味に大きい前進です。

実際に試すには:uvでサクッとインストール

ソースからビルドする方法もありますが、uvを使えば楽に試せるケースがあります。Python 3.13ではフリースレッド版のビルドに「t」サフィックス(例:python3.13t)が使われています。3.14でも同様の表記(例:3.14t)が使われることが多いようです。

# フリースレッド版 Python 3.14 のインストール
uv python install 3.14t

# インストール確認
uv run --python 3.14t python -c "import sys; print(sys._is_gil_enabled())"

sys._is_gil_enabled()False を返せばGILが無効になっています。

ビルド時にフリースレッドモードかどうかを確認したい場合は、以下のようなコードでチェックできます:

import sysconfig, sys

print("Free-threaded build?:", sysconfig.get_config_var("Py_GIL_DISABLED"))
print("GIL enabled at runtime?:", sys._is_gil_enabled())

フリースレッドモードが有効なときはプログラムのシングルスレッド性能が環境次第で低下します。公式ドキュメント上は、pyperformance平均でだいたい1~8%程度のオーバーヘッドとされています。つまり、並列処理は書きやすくなる一方で逐次処理は少し遅くなります。この点は念頭に置いておく必要があります。

フリースレッドで何ができるようになるか:コード例

従来のGIL有効なPythonでCPU-boundな処理をマルチスレッドで走らせても、実質的には直列実行と変わりませんでした。フリースレッドモードでは、これが本当の意味での並列実行になります。

import threading
import time
import os

def heavy_task(n):
    """CPUをガリガリ使う処理"""
    total = 0
    for i in range(n):
        total += i * i
    return total

def run_with_threads(num_threads, iterations):
    threads = []
    for _ in range(num_threads):
        t = threading.Thread(target=heavy_task, args=(iterations,))
        threads.append(t)

    start = time.time()
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    return time.time() - start

num_threads = os.cpu_count()
elapsed = run_with_threads(num_threads, 10_000_000)
print(f"スレッド数: {num_threads}, 実行時間: {elapsed:.2f}秒")

GIL有効なPythonでこれを動かすと、スレッド数を増やしてもほぼ同じ時間がかかります。フリースレッドモードでは、CPU数に応じてスケールアップするのが確認できるはずです。実際に自分の環境でも体感できるくらい差が出ました。

CPUバウンドなタスクは、GILフリーの恩恵をもっとも受けられる処理パターンです。機械学習の前処理や、数値計算を複数スレッドで分割したいケースが典型的なユースケースになると思います。

注意点:「フリースレッド=とりあえず全部速くなる」ではない

ここが大事なポイントなので少し丁寧に書きます。

スレッドセーフな書き方が必要になる

GILがあったおかげで「なんとなく動いていた」コードが壊れる可能性があります。複数のスレッドからグローバル変数などを操作する際は、threading.Lockを使って適切に保護する「スレッドセーフ」なコーディングがより重要になります。

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    with lock:  # ここ注意:フリースレッドでは必須
        counter += 1

GIL有効なときは += がなんとなく安全だったのは、GILが暗黙のロックになっていたから。その前提が崩れるので、レースコンディションには気をつける必要が出てきます。

サードパーティライブラリの対応状況が課題

C拡張ライブラリの中にはGILに依存していた古いものもあり、フリースレッド環境で問題を起こす可能性があります。使用しているライブラリの対応状況を確認しましょう。主要ライブラリは対応が進んでいますが、全部が対応済みというわけではないので、本番環境に入れる前はちゃんと確認が必要です。

GCとメモリまわりの内部的な変化

フリースレッドモードが有効なCPythonでは、メモリ管理やスレッドセーフ化のために内部実装がいろいろ変わっています。ワークロードによってはメモリ効率が良くなるケースもあるようですが、ここは実際の負荷で計測してみないとわからない部分が多いのが正直なところです。

今後の見通し:フェーズ3はいつ?

フリースレッドモードのPythonはオプション扱いとなっており、ソースからビルドする場合は --disable-gil を指定してfree-threadedビルドを作る形になります。既定で提供されるのは、従来のGILを採用したPythonとなります。

フェーズ3においてはフリースレッドモードが既定となる可能性も語られていますが、これが将来のPythonで実現するかは、現時点では未定とされています。エコシステム全体の互換性を保ちながら移行するのは相当大変そうで、主要ライブラリのフリースレッド対応が進んでいくのを見ながら、慎重に進める雰囲気を感じます。

個人的には「フリースレッドが実用になった」という意味では3.14は大きな節目だと思っていて、本番で使うかはともかく、手元で試して感覚をつかんでおくのは良さそうです。自分もLambdaでCPU-boundな処理をちょこちょこ書いているので、multiprocessingの代わりにスレッドで書けるようになると嬉しい場面はあるな、と思っています。フリースレッドのPython、まだ「雰囲気は掴んだ」くらいの理解ですが、実際のワークロードにどう活かすかはもう少し手を動かしながら考えていくつもりです。

※この記事にはプロモーションが含まれます

ちなみに、お名前.com レンタルサーバー(WordPressに特化した高速レンタルサーバー。月額990円〜、独自ドメイン実質0円)も気になっています。お名前.com レンタルサーバー

まとめ

Python 3.14でフリースレッド(GIL無効化)が実装され、マルチスレッドでのCPU-bound処理が本当の意味で並列実行できるようになりました。シングルスレッド時のオーバーヘッドも1~8%程度と改善が進んでいます。

ただし、スレッドセーフなコーディングやライブラリ対応の確認がより重要になります。デフォルトではまだGIL有効版ですが、uvなど簡単にフリースレッド版を試せるようになったので、手元で実際に試してみるのをお勧めします。

参考になったらクリックしてもらえると嬉しいです!

Blogmura ProgrammingProgramming Ranking
タイトルとURLをコピーしました