2024年10月7日にリリースされたPython 3.13の概要を紹介します。
Python 3.13ではPythonのプログラミング言語としての仕様に大きな変更はありませんでしたが、将来のPythonの実行環境に大きな影響を与える、大きな変更が導入されました。
PEP 703: フリースレッドモード¶
Python 3.13では、実験的な機能として、フリースレッドモードが導入されました。従来のPythonには、グローバルインタプリタロック(GIL)があり、マルチスレッドでのパフォーマンスが向上しないという問題がありました。フリースレッドモードは、この制約を解消し、マルチスレッドでのパフォーマンスを向上させることを目的としています。
フリースレッドモードについては、PEP 703 グローバル・インタプリタ・ロックをオプション化 で詳しく解説していますので、参照してください。
フリースレッド版Pythonのインストール¶
フリースレッド版をソースコードからビルドする場合は、configureのオプションとして --disable-gil
を指定します。Windows と macOSでは、公式インストーラでオプションを指定してフリースレッド版をインストールできます。
フリースレッド版Pythonは python3.13t
・python3t
のように、t
を付加したファイル名でインストールされるので、通常のPythonと共存してインストールできます。
フリースレッドモードのパフォーマンス¶
次のコードを使って、フリースレッドモードのパフォーマンスを測定してみましょう。
from concurrent.futures import ThreadPoolExecutor
def calc():
a = [*range(100000)]
b = [*range(100000)]
c = sum([a**2+b**2 for a, b in zip(a, b)])
def run(workers):
with ThreadPoolExecutor(workers) as exc:
for i in range(20):
exc.submit(calc)
※ 動作環境: AWS EC2 c6i.8xlarge (32 vCPU, 64GB RAM)
フリースレッドモードのPythonでは、スレッド数に応じてパフォーマンスが向上することが確認できます。
フリースレッドのトレードオフ¶
次に、先程のプログラムを少しだけ修正してパフォーマンスを測定してみましょう。次のプログラムでは、a
とb
をグローバル変数として定義しています。
from concurrent.futures import ThreadPoolExecutor
a = [*range(100000)]
b = [*range(100000)]
def calc():
c = sum([a**2+b**2 for a, b in zip(a, b)])
def run(workers):
with ThreadPoolExecutor(workers) as exc:
for i in range(20):
exc.submit(calc)
元の処理ではcalc()
を呼び出す毎に a
とb
を生成していましたが、このプログラムではa
とb
をグローバル変数として一度だけ作成しています。このため、通常の Python3.13 の平均処理時間は360ms->234msと短縮されています。
一方、フリースレッドモードでは、データの生成処理がなくなったにも関わらず calc()
の実行時間が増加していることが確認できます。これは、同じスレッドで作成されたオブジェクトに対してはロックが最小限になるように最適化されていますが、別のスレッドで作成したオブジェクトの場合は多数のロックが発生するため、パフォーマンスが低下することが主な原因のようです。
フリースレッドの利用によって処理時間は短縮されることが期待されますが、効率的な処理を行うためにはPythonがどのように動作するかを理解しておくことが重要になりそうです。