ホームページ > バックエンド開発 > Python チュートリアル > Python GIL を使用しない高性能マルチスレッドへのゲートウェイ

Python GIL を使用しない高性能マルチスレッドへのゲートウェイ

Mary-Kate Olsen
リリース: 2025-01-06 16:58:41
オリジナル
657 人が閲覧しました

導入

Python はその使いやすさと多用途性で長い間知られてきましたが、Python コミュニティで多くの議論を巻き起こしているトピックの 1 つは Global Interpreter Lock (GIL) です。 GIL は、Python の同時実行モデル、特に複数の CPU コアを活用できる CPU バウンドのタスクにとって、安全装置でもありボトルネックでもあります。しかし、Python 3.13 のリリースにより、Python 開発者は GIL を無効にする機能という画期的な新しいオプションを利用できるようになりました。このブログでは、GIL とは何か、GIL がマルチスレッドのパフォーマンスの障害となっている理由、真のマルチスレッド パフォーマンスを解放するために Python 3.13 で GIL を検出して無効にする方法について説明します。

グローバル インタープリター ロック (GIL) とは

グローバル インタープリター ロック (GIL) は、Python オブジェクトへのアクセスを保護するミューテックスであり、複数のネイティブ スレッドが Python バイトコードを同時に実行するのを防ぎます。これにより、Python プログラムのスレッドの安全性が確保されますが、同時実行が犠牲になります。 GIL により、I/O バウンドのタスクでは Python スレッドの効率が向上しますが、CPU バウンドのタスクではパフォーマンスが制限されます。

GIL がマルチスレッドのボトルネックになる理由

Python の GIL では、マルチスレッド プログラムであっても、同時に実行できるスレッドは 1 つだけです。これは、プログラムが入出力操作を待機している I/O バウンドのタスクには問題ありませんが、数値処理、データ分析、画像処理などの CPU バウンドのタスクのパフォーマンスは大幅に制限されます。

Python 3.13: GIL を無効にしてマルチスレッドのロックを解除する

Python 3.13 では、開発者は Python ビルド プロセス中に GIL を無効にするオプションを利用できます。ただし、事前に構築された Python ディストリビューションでは、GIL を無効にすることはできません。代わりに、--disable-gil オプションを使用してソースから Python 3.13 をコンパイルする必要があります。

この新しいオプションは、CPU バウンドのマルチスレッド タスクにおける真の並列処理への扉を開き、スレッドを複数のコア間で並列実行できるようにします。

GIL なしで Python 3.13 を使用するための前提条件

  • Python 3.13 ソース コード: 標準のビルド済みバイナリでは GIL を無効にすることはできません。 --disable-gil フラグを使用してソースから Python 3.13 をビルドする必要があります。
  • マルチコア CPU: スレッドは複数のコア間で並行して実行されるため、真のマルチスレッドのメリットを得るにはマルチコア CPU が必要です。

GIL を無効にして Python 3.13 をコンパイルする

-X gil=0 フラグを使用して GIL を無効にするには、--disable-gil フラグを有効にしてソースから Python をコンパイルする必要があります。その方法は次のとおりです

ステップバイステップ

  • Python 3.13 ソース コードをダウンロード まず、Python 3.13 ソース コードの tarball を Python の公式 Web サイトからダウンロードする必要があります。これは、事前に構築されたバイナリ (python.org から直接ダウンロードしたバイナリなど) が、GIL の無効化をサポートしてコンパイルされていないためです。 Web ブラウザー、wget、またはターミナルでcurlを使用してダウンロードできます。
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
ログイン後にコピー
  • --disable-gil を使用してビルドを構成する GIL を無効にするオプションをサポートするには、--disable-gil を使用して Python を構成する必要があります。
./configure --disable-gil
ログイン後にコピー
  • Python をコンパイルしてインストールします。
make
sudo make altinstall 
ログイン後にコピー
  • altinstall ステップが失敗した場合は、--prefix を指定してconfigureコマンドを再実行します。
./configure --disable-gil --prefix=$HOME/python3.13
ログイン後にコピー
  • 指定したディレクトリで make altinstall を実行します 次に、make altinstall コマンドを実行します。
make altinstall
ログイン後にコピー

Python 3.13 で GIL を検出する方法

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()
ログイン後にコピー

ハンズオン: GIL を使用した Python マルチスレッドと GIL フリーの Python マルチスレッド

次の 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 The Gateway to High-Performance Multithreading Without GIL

Python The Gateway to High-Performance Multithreading Without GIL

このグラフは、Python 3.13 で GIL を無効にすると、マルチスレッドの CPU バウンド タスクのパフォーマンスが大幅に向上し、Python が複数の CPU コアを効率的に並行して利用できるようになることを明確に示しています。シングルスレッドおよびマルチ処理のパフォーマンスはほとんど影響を受けませんが、マルチスレッドのパフォーマンスは大幅な向上を示しており、Python 3.13 はマルチスレッドに依存する CPU 集中型のアプリケーションにとって大きな変革をもたらします。

ただし、3.13 より前の Python バージョンは GIL の無効化をサポートしていません。これが、GIL を有効にした場合のマルチスレッド パフォーマンスが Python 3.13 のパフォーマンスと同等のままである理由を説明しています。以前のバージョンのこの制限により、CPU に依存するタスクでマルチスレッドを完全に活用する Python の機能が引き続き制限されます。

GIL を無効にする前の重要な考慮事項

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 リポジトリ

このブログの例の完全なソース コードは、私の GitHub で見つけることができます。

Python GIL パフォーマンス分析

免責事項:

これは個人のブログです。ここで表明された見解や意見は著者のみのものであり、著者が職業上または個人的に関係する組織や個人の見解や意見を代表するものではありません。

以上がPython GIL を使用しない高性能マルチスレッドへのゲートウェイの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート