Python はその使いやすさと多用途性で長い間知られてきましたが、Python コミュニティで多くの議論を巻き起こしているトピックの 1 つは Global Interpreter Lock (GIL) です。 GIL は、Python の同時実行モデル、特に複数の CPU コアを活用できる CPU バウンドのタスクにとって、安全装置でもありボトルネックでもあります。しかし、Python 3.13 のリリースにより、Python 開発者は GIL を無効にする機能という画期的な新しいオプションを利用できるようになりました。このブログでは、GIL とは何か、GIL がマルチスレッドのパフォーマンスの障害となっている理由、真のマルチスレッド パフォーマンスを解放するために Python 3.13 で GIL を検出して無効にする方法について説明します。
グローバル インタープリター ロック (GIL) は、Python オブジェクトへのアクセスを保護するミューテックスであり、複数のネイティブ スレッドが Python バイトコードを同時に実行するのを防ぎます。これにより、Python プログラムのスレッドの安全性が確保されますが、同時実行が犠牲になります。 GIL により、I/O バウンドのタスクでは Python スレッドの効率が向上しますが、CPU バウンドのタスクではパフォーマンスが制限されます。
Python の GIL では、マルチスレッド プログラムであっても、同時に実行できるスレッドは 1 つだけです。これは、プログラムが入出力操作を待機している I/O バウンドのタスクには問題ありませんが、数値処理、データ分析、画像処理などの CPU バウンドのタスクのパフォーマンスは大幅に制限されます。
Python 3.13 では、開発者は Python ビルド プロセス中に GIL を無効にするオプションを利用できます。ただし、事前に構築された Python ディストリビューションでは、GIL を無効にすることはできません。代わりに、--disable-gil オプションを使用してソースから Python 3.13 をコンパイルする必要があります。
この新しいオプションは、CPU バウンドのマルチスレッド タスクにおける真の並列処理への扉を開き、スレッドを複数のコア間で並列実行できるようにします。
-X gil=0 フラグを使用して GIL を無効にするには、--disable-gil フラグを有効にしてソースから Python をコンパイルする必要があります。その方法は次のとおりです
wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tgz
tar -xf Python-3.13.0.tgz cd Python-3.13.0
./configure --disable-gil
make sudo make altinstall
./configure --disable-gil --prefix=$HOME/python3.13
make altinstall
Python 3.13 では、sys._is_gil_enabled() 関数を使用して GIL が有効か無効かを確認できます。
import sys def check_gil_status(): if sys.version_info >= (3, 13): status = sys._is_gil_enabled() if status: print("GIL is currently enabled.") else: print("GIL is currently disabled.") else: print("Python version does not support GIL status detection.") check_gil_status()
次の Python コードは、Python 3.13 で GIL を無効にした場合のパフォーマンスの向上を評価するために開発されました。このスクリプトは 8 つのスレッドを同時に実行し、それぞれのスレッドに大きな数の素因数を計算するタスクが割り当てられます。真の並列処理を活用することで、コードは GIL なしで達成されるパフォーマンスの向上を強調しています。
#!/usr/bin/env python3 import sys import sysconfig import time from threading import Thread from multiprocessing import Process # Decorator to measure execution time of functions def calculate_execution_time(func): def wrapper(*args, **kwargs): start_time = time.perf_counter() result = func(*args, **kwargs) end_time = time.perf_counter() execution_time = end_time - start_time print(f"{func.__name__} took {execution_time:.4f} seconds.") return result return wrapper # Compute-intensive task: Iterative Fibonacci calculation def compute_fibonacci(n): """Compute Fibonacci number for a given n iteratively.""" a, b = 0, 1 for _ in range(n): a, b = b, a + b return a # Single-threaded task execution @calculate_execution_time def run_single_threaded(nums): for num in nums: compute_fibonacci(num) # Multi-threaded task execution @calculate_execution_time def run_multi_threaded(nums): threads = [Thread(target=compute_fibonacci, args=(num,)) for num in nums] for thread in threads: thread.start() for thread in threads: thread.join() # Multi-processing task execution @calculate_execution_time def run_multi_processing(nums): processes = [Process(target=compute_fibonacci, args=(num,)) for num in nums] for process in processes: process.start() for process in processes: process.join() # Main execution function def main(): # Check Python version and GIL status for Python 3.13+ print(f"Python Version: {sys.version}") py_version = float(".".join(sys.version.split()[0].split(".")[:2])) status = sysconfig.get_config_var("Py_GIL_DISABLED") if py_version >= 3.13: status = sys._is_gil_enabled() if status is None: print("GIL cannot be disabled for Python <= 3.12") elif status == 0: print("GIL is currently disabled") elif status == 1: print("GIL is currently active") # Run tasks on the same input size for comparison nums = [300000] * 8 print("\nRunning Single-Threaded Task:") run_single_threaded(nums) print("\nRunning Multi-Threaded Task:") run_multi_threaded(nums) print("\nRunning Multi-Processing Task:") run_multi_processing(nums) if __name__ == "__main__": main()
## Python 3.13 with GIL Disabled Python Version: 3.13.0 experimental free-threading build (main, Oct 14 2024, 17:09:28) [Clang 14.0.0 (clang-1400.0.29.202)] GIL is currently disabled Running Single-Threaded Task: run_single_threaded took 8.6587 seconds. Running Multi-Threaded Task: run_multi_threaded took 1.3885 seconds. Running Multi-Processing Task: run_multi_processing took 1.5953 seconds. ## Python 3.13 with GIL Enabled Python Version: 3.13.0 experimental free-threading build (main, Oct 14 2024, 17:09:28) [Clang 14.0.0 (clang-1400.0.29.202)] GIL is currently active Running Single-Threaded Task: run_single_threaded took 8.7108 seconds. Running Multi-Threaded Task: run_multi_threaded took 8.6645 seconds. Running Multi-Processing Task: run_multi_processing took 1.4530 seconds. ## Python 3.12 Python Version: 3.12.6 (main, Sep 7 2024, 19:30:10) [Clang 14.0.0 (clang-1400.0.29.202)] GIL cannot be disabled for Python <= 3.12 Running Single-Threaded Task: run_single_threaded took 8.7004 seconds. Running Multi-Threaded Task: run_multi_threaded took 8.6297 seconds. Running Multi-Processing Task: run_multi_processing took 1.4876 seconds.
マルチスレッドのパフォーマンス: GIL を無効にすることの本当の利点は、マルチスレッドのシナリオで明らかです:
GIL が無効になっている場合 (3.13)、実行時間は 1.5703 秒です。
GIL が有効になっている場合 (3.13)、実行時間は 8.5901 秒です。
結果: GIL を無効にすると、マルチスレッド タスクのパフォーマンスが約 81.7% 向上しました。
このグラフは、Python 3.13 で GIL を無効にすると、マルチスレッドの CPU バウンド タスクのパフォーマンスが大幅に向上し、Python が複数の CPU コアを効率的に並行して利用できるようになることを明確に示しています。シングルスレッドおよびマルチ処理のパフォーマンスはほとんど影響を受けませんが、マルチスレッドのパフォーマンスは大幅な向上を示しており、Python 3.13 はマルチスレッドに依存する CPU 集中型のアプリケーションにとって大きな変革をもたらします。
ただし、3.13 より前の Python バージョンは GIL の無効化をサポートしていません。これが、GIL を有効にした場合のマルチスレッド パフォーマンスが Python 3.13 のパフォーマンスと同等のままである理由を説明しています。以前のバージョンのこの制限により、CPU に依存するタスクでマルチスレッドを完全に活用する Python の機能が引き続き制限されます。
Python 3.13 で Global Interpreter Lock (GIL) を無効にすると、マルチスレッドの CPU 依存タスクのパフォーマンスが大幅に向上します。ただし、そうする前に考慮すべき重要な要素がいくつかあります。
スレッド セーフティ: GIL を使用しない場合、コード内の競合状態を防ぐために、ロックまたはその他の同期メカニズムを使用してスレッド セーフティを手動で処理する必要があります。
潜在的なパフォーマンス低下: きめ細かいロックにより競合が発生する可能性があり、以前は GIL の恩恵を受けていたシングルスレッドまたは I/O バウンドのタスクのパフォーマンスが低下する可能性があります。
サードパーティ ライブラリとの互換性: 多くの C 拡張機能およびライブラリは、スレッド セーフのために GIL の存在を前提としています。 GIL を無効にするには、これらのライブラリがマルチスレッド環境で正しく動作するように更新する必要がある場合があります。
複雑なメモリ管理: GIL を無効にすると、メモリ管理がより複雑になり、スレッドセーフなメモリ処理が必要になり、バグやエラーのリスクが増加する可能性があります。
I/O バウンド タスク: GIL を無効にすると、I/O バウンド タスクの利点は限定的ですが、asyncio などのノンブロッキング I/O メカニズムの方が効果的である可能性があります。
デバッグの難しさ: GIL を使用しないと、競合状態やデッドロックが発生する可能性が高くなるため、マルチスレッド アプリケーションのデバッグがより困難になる可能性があります。
メモリ使用量の増加: GIL を使用せずにロックを使用したりスレッド状態を管理すると、特にマルチスレッド アプリケーションでメモリ消費量が増加する可能性があります。
組み込みシステム: GIL を無効にすると、組み込みシステムにおける Python のマルチスレッド環境との統合が複雑になる可能性があり、効果的な統合にはより多くの労力が必要になります。
ロック競合: 場合によっては、GIL を無効にするとスレッド間でロック競合が発生し、期待されるパフォーマンスの向上が低下する可能性があります。
このブログの例の完全なソース コードは、私の GitHub で見つけることができます。
Python GIL パフォーマンス分析
これは個人のブログです。ここで表明された見解や意見は著者のみのものであり、著者が職業上または個人的に関係する組織や個人の見解や意見を代表するものではありません。
以上がPython GIL を使用しない高性能マルチスレッドへのゲートウェイの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。