Pythonのグローバル・インタープリタ・ロック(GIL)とは?
Pythonの領域では、グローバルインタープリタロック(GIL)として知られる悪名高い生物が潜んでおり、パフォーマンスやスレッドに影響を与えています。プログラマーがGILを理解し、挑戦し、超越する可能性を求めてPythonの心臓部に踏み込むとき、彼らはその限界と、データサイエンスや機械学習を含む様々な領域におけるPythonの実装への影響の両方に遭遇する。
GILの無効化に関するRedditorzurtexのアップデート
-disable-gilは
CPython 3.12には適用されません。実際、CPython 3.12ブランチはrc1リリースの準備のため、リリース管理チーム以外のコミットにはロックされています。
さらに、このような大きな変更は main (現在 3.13) にのみ反映され、マイナーバージョンブランチが main から分割されたときに直接反映されることはありません。
PEP 703はCPython 3.13に載るかもしれませんが、運営評議会はCPython 3.14までずれ込んでも問題ないとはっきり言っています。ですから、さらに「すぐに」着地するというのは間違っているかもしれません:
PEP 703 は草案の状態であり、まだ正式に受け入れられていません。
Sam Grossは現在休暇中で、nogilブランチをmainにリベースし、少なくともFaster CPythonチームが提起した、純粋なPythonコードでクラッシュする可能性のあるいくつかの問題に対処しなければなりません。
CPython 3.12 はグローバルインタープリタロック (GIL) を無効にする
./configure
スクリプトはすぐに Python/patchlevel.h
ファイルにPy_NOGIL
マクロを設定するかもしれません。つまり、GILを無効にする方法は次のようになります:
git clone -b nogil-3.12 https://github.com/colesbury/nogil-3.12.git
cd nogil-3.12
./configure --disable-gil
make
disable-gil
フラグはPEP draftであることに注意して ください。 disable-gilフラグはPEP draftであることに注意してください。 認識されないと書かれていても、上記のコマンドはGILを無効にしたバイナリを作成します。
ビルド時にGILを無効にする: https://peps.python.org/pep-0703/#build-configuration-changes
実行時にGILを再度有効にする:https://peps.python.org/pep-0703/#pythongil-environment-variable
Python GILの起源と目的、Global Interpreter Lockの役割
もともとPython 1.5で導入されたGILは、安定性を向上させるために設計されたメカニズムでした。これは本質的に、一度に1つのスレッドだけがPythonバイトコードを実行できるようにするミューテックス(またはロック)です。これは主にマルチスレッドに対応し、メモリ管理を容易にし、Python環境の安全性と予測可能性を生み出すためのソリューションでした。
Pythonグローバルインタープリターロック(GIL)の限界
しかし、この安全機構には代償が伴いました。本来の意図とは裏腹に、GILはパフォーマンスの阻害要因となり、I/O操作やシステムコール時にPythonプログラムが応答しなくなる原因となりました。
Pythonのマルチスレッディングは、このロックのためにCPU負荷の高いタスクでは事実上役に立たなくなり、大きな障害となった。シンプルで安全であるにもかかわらず、私たちはこの制限に挑戦し、克服する冒険をすることになった。
非同期ライブラリとマルチプロセシング:Pythonの並行処理の親友
GIL自体に挑戦する前に、最初の対応はその制限を緩和することでした。並行性の高いタスクのために、非同期I/Oライブラリが導入されました。これらのライブラリはGILに干渉することなくI/O操作やシステムコールができるようにします。
CPUに負荷のかかる作業に対しては、マルチプロセッシングが登場し、複数のPythonランタイムを同時に操作できるようになった。これは、スレッドが通信してワークロードを共有できるメッセージパッシング機構によって可能になった。これらのツールはGILの回避策を提供し、GILを回避できるようにした。CPythonの代替となるIronPythonや Jythonを調べると、マルチスレッドの扱い方が異なることがわかります。例えばIronPythonは.NETフレームワークと統合され、そのスレッドモデルから利益を得ますが、Java仮想マシン上で動作するJythonはJavaのスレッド機能を活用します。Pythonのこれらの実装は、並行処理へのさまざまなアプローチへの洞察を提供し、さまざまなプラットフォームや環境に適応するプログラミング言語の多様性を示しています。
py.GILを無効にするという決定:準備、挑戦の始まり
CPython 3.12のリリースで、旅はエキサイティングな展開を迎えました。disable-gil
フラグでPythonを再コンパイルする必要があるこのベンチャーは、劇的なパフォーマンス向上の可能性を約束しました。しかし、それは同時に私たちをより危険な領域へと駆り立てました。このような急進的な動きはどのような意味を持つのでしょうか?
PythonのGIL無効化の意味:安全性が失われる
グローバルインタープリターロックを無効にすることで、複数のスレッドが同時にPythonバイトコードを実行できるようになった。これは、パフォーマンスを大幅に向上させる可能性があることを意味する。Pythonに内蔵されている安全カバーが完全に取り除かれ、共有リソースやメモリ処理の問題によるクラッシュの可能性が出てきたのです。
Pythonの本当のスレッド:本当の報酬
GILを無効にしたことで、より斬新なスレッドの使い方を考えることができるようになった。CPU負荷の高いタスクでさえもスレッド間で分割することができ、これはこの試練の重要な報酬と考えられる。しかし、これには共有メモリとリソースを注意深く扱う必要があり、そうでなければ大惨事になりかねません。
Python GILに関する収穫と結論
GILを無効にするスリルとチャンスにもかかわらず、Pythonのオリジナルの設計にはメリットがあることが明らかになりました。その限界は目に余るものでしたが、GILは潜在的なクラッシュからの保護も提供し、システム全体の安定性を確保していました。
GILを無効にすることでパフォーマンスが向上する可能性はあるが、それにはかなりのリスクが伴う。この新しい領域に踏み込む前に、適切な理解と予防措置が必要である。GILを完全に無効にするよりも、Pythonのスレッドモデル内で最適化し、非同期I/Oライブラリやマルチプロセッシングを活用する方が賢明です。
PythonのGILへの冒険は、潜在的な解決策をもたらすだけでなく、システム全体に対するより深い理解をもたらし、Pythonとのより思慮深く複雑な関わり方を促進します。手元にある最高のツールを使い、GILによるパフォーマンスペナルティを回避することは可能です。
Python GILについての私の考え
私は当初、Pythonのグローバル・インタープリタ・ロック(GIL)を無効にする可能性に興奮していた。しかし、よく考えてみると、そうではないと思う。GILを無効にすると、Python内でスレッドパターンとスレッドが使えるようになるだけだ。高い並行性と効率的なプログラムを実現するために私が好む方法は、メッセージ・パッシングを用いた複数のプロセスを使用することです。
これがPubNubの設計方法であり、現在世界中に多くのKubernetesクラスタを分散させ、ネットワーク上の10億台以上のデバイスにサービスを提供している。私たちは、クラウド・プロバイダーであるAmazon Web Servicesから購入したリソースをフルに活用できるパターンに従うことで、このレベルの並行性とスケールを実現することができます。このデザインパターンは、LinuxのepollカーネルAPIを通じて直接非同期I/Oカーネル割り込みを使用し、C言語で実装されているため、スレッド、ロック、メモリ安全性について心配する必要はありません。これにより、コンテキスト・スラッシングを行うことなく、CPUをフルに活用することができる。
Pythonでも同じ効果が得られる。シングルスレッドのPythonワーカーをDockerコンテナで起動し、複数のDockerコンテナをスピンアップします。Kubernetesエコシステムで使われている一般的な標準ロードバランサーであるNginxなどのロードバランサーを使って、ラウンドロビンやロードバランスを行うことができる。これで必要なことが実現できる。PythonエンジンがI/O待ちの間にロックしないように、非同期I/Oライブラリを追加したいでしょう。これによって、購入したハードウェアを最終的に最大限に活用できるようになる。
ラップトップのような単一のシステムで並行処理を利用したい場合は、Pythonマルチプロセッシングライブラリを使うのがよいでしょう。システム上のCPUコアにワークロードを分散するメカニズムを提供します。
Python 3.12 GIL よくある質問
次にできることに関して、いくつかの追加項目をカバーする必要があります。以下はディスカッションの中で出てきたよくある質問です。
Python 3.12 と新しい --disable-gil フラグに興奮すべきですか?
もしあなたがスレッドやチャレンジや余分なコードへの取り組みが好きなら、そうです。
そうでなければダメです。もっと良い選択肢があります!
Python 3.12 と新しい--disable-gil
フラグに興奮するかどうかは、あなたの特定のユースケースとマルチスレッドの複雑さの把握に大きく依存します。
CPUに負荷のかかる作業を頻繁に行い、慎重なスレッド管理とプログラミングに精通しているのであれば、グローバルインタープリタロック(GIL)を無効にするオプションは有益でしょう。この機能によって、複数のスレッドが GIL によって遅くされることなく Python バイトコードを同時に実行できるため、パフォーマンスが劇的に向上する可能性があります。
この機能はリスクも増加させます。PythonのGILは、複数のスレッドが同時にPythonバイトコードを実行するのを防ぐことで、Pythonオブジェクトの完全性を維持する上で重要な役割を果たしています。GILを使わずにスレッドプログラムを開発するには、スレッドセーフと同期についてよく理解する必要があります。
多くの標準PythonライブラリはスレッドセーフのためにGILの存在に依存しており、GILを無効にした場合、これらは正しく、あるいはまったく機能しないかもしれません。
そのため、この機能は特定のコンテキストでは大幅な性能向上をもたらす可能性がありますが、軽々しく、あるいは普遍的に使用するものではありません。どんな強力なツールでもそうであるように、正しく効果的に使うには理解と敬意と注意が必要です。
ほとんどのPython開発者、特にCPU負荷の高いマルチスレッドアプリケーションを開発していない開発者にとっては、Python標準ライブラリの非同期ライブラリとマルチプロセッシングモジュールの登場が、並行処理と並列処理を扱うための十分なメカニズムを提供し、GILを無効にすることの意味を薄くしています。
GILを無効にすると何が使えるようになるのか?
グローバルインタープリタロック(GIL)を無効にすることで、主にマルチコアPythonプログラムが真の並行スレッド実行を実現できるようになり、CPUバウンドタスクの大幅な性能向上につながる可能性があります。
以下は GIL を無効にしたときに利用できるようになることを簡単にまとめたものです:
同時スレッド実行: GILを無効にすると、複数のスレッドがロックを待つことなくPythonバイトコードを同時に実行できるようになります。これはマルチコアプロセッサのより良い利用につながる可能性があります。
マルチスレッドCPUバウンドタスクの性能向上: 複数のスレッドに分割された CPU バインドタスクでは、GIL を無効にすることで、各スレッドが異なるコアで同時に実行できるため、大幅な性能向上につながる可能性があります。
スレッド同期の制御性向上: GILを無効にするということは、開発者自身がスレッド同期を処理しなければならないことを意味する。これにはマルチスレッドに関する深い理解が必要ですが、スレッド操作の制御がより容易になり、より微妙で最適化されたパフォーマンス・チューニングが可能になる可能性があります。
潜在的なマイナス面を認識しておくことは重要である。GILを無効にすると、多くのPythonのライブラリや操作のスレッドセーフな動作を保証するセーフガードがなくなります。そのため、競合状態やデッドロック、その他の同期の問題など、マルチスレッドに関連する複雑さや潜在的な落とし穴は、開発者の責任で対処・管理することになります。
GILを無効にするとパフォーマンスが向上しますか?
グローバルインタープリタロック(GIL)を無効にすることで性能向上が見られるかどうかは、Pythonプログラムの性質に依存します。
あなたのプログラムがCPUバウンドタスクを多用し、マルチスレッドを使って設計されている場合、大幅な性能向上が見られる可能性があります。GIL を無効にすると、複数のスレッドが異なるコアで Python バイトコードを同時に実行できるようになり、マルチコアプロセッサをよりよく利用できるようになるからです。
あなたのプログラムが主にI/Oバウンドしている場合(例えば、ネットワーク応答を待ったり、ディスクから読み込んだりしている時間がほとんどです)、GILがボトルネックになる可能性は低く、GILを無効にしてもパフォーマンスの改善はほとんどないでしょう。実際、I/Oバウンドのシナリオでは、非同期プログラミングや並行処理ライブラリを適切に活用する方が、GILを無効にするよりも有益な場合がある。
プログラムがスレッド同期を正しく管理するように設計されていない場合、GILを無効にすると、実際にパフォーマンスが低下したり、競合状態やその他のマルチスレッド関連の落とし穴による予期せぬ動作が発生したりする可能性がある。
特定のシナリオでは GIL を無効にすることで性能が向上する可能性がありますが、GIL を使わずに Python で真のマルチスレッドを安全かつ効果的に管理するには、かなりの注意と専門知識が必要です。多くのアプリケーションでは、マルチプロセッシングモジュールや非同期IOのようなPythonの他の機能を活用して並列タスクや並行タスクを管理する方が、安全で効果的なアプローチかもしれません。
GILを無効にするには?
PythonのGIL(Global Interpreter Lock)を無効にするには、基本的に-disable-gilフラグを付けてPythonをビルドします。前提条件として、この方法では Python をソースからコンパイルする必要があります。さらに、GILを無効にする機能はCPython v3.14以降にあることを覚えておいてください。以下に手順を示します:
1.1.Python3.1X以降のCPythonソースファイルをダウンロードする。Python 3.14の可能性が高い。Githubからリポジトリをcloneできます。
git clone -b 3.1X https://github.com/python/cpython.git
2.ダウンロードしたCPythonのディレクトリに移動する。
cd cpython
3.configureスクリプトを-disable-gilフラグを付けて実行する。
./configure --disable-gil
4.Pythonをビルドする。
make
コードが適切にスレッドセーフでない場合、スレッド問題を引き起こす可能性があるため、軽々しく行うべきではありません。これは、特に真のマルチスレッドが必要で、Pythonがスレッドセーフを保証していない場合に発生する複雑さに対処する準備ができている開発者に適しています。GIL を無効にする前に、あなたのコードと使用するライブラリがスレッドセーフであることを常に確認してください。
環境変数を使って GIL を再び有効にするにはどうすればいいですか?
Python Global Interpreter Lock (GIL) は PYTHONGIL と呼ばれる環境変数を 1 に設定することで再び有効にすることができます。
この変数の設定は、オペレーティング・システムによって異なる方法で行うことができる。以下は、LinuxやMacOSを含むUnixベースのシステムでの設定方法です:
export PYTHONGIL=1
python my-app.py
Windows システムでは、コマンドを使って環境変数を設定できます:
set PYTHONGIL=1
python my-app.py
これらのコマンドは現在のターミナルセッションに対してのみ環境変数を設定することに注意してください。永続的に設定したい場合は、シェルの初期化ファイル(Unixシステムのbashシェルの.bashrcや.bash_profileのような)に適切なコマンドを追加するか、Windowsのシステムプロパティで変数を設定する必要があります。
この環境変数の効果を確認するには、この環境変数を設定した同じターミナルセッションから Python プログラムを実行する必要があることを覚えておいてください。
GILを無効にするリスクはありますか?
はい、Pythonでグローバルインタープリタロック(GIL)を無効にすることにはかなりのリスクがあります。以下が主なリスクです:
スレッドセーフの問題: スレッドセーフの問題: GILは、複数のスレッドが同時にPythonバイトコードを実行することを許さないことで、ある程度のスレッドセーフを提供します。GILを無効にすることで、スレッドセーフを確保する責任は開発者にあります。複数のスレッドが適切な同期を取らずに同時に共有データにアクセスしたり変更したりすると、競合状態やデータ破損、クラッシュなどの問題が発生する可能性があります。
互換性の問題: Pythonライブラリの多くは、標準ライブラリも含めて、GILが有効であることを前提に書かれています。これらのライブラリはスレッドセーフでない操作を使用することがあり、GILの下では安全ですが、GILが無効な場合に問題を引き起こす可能性があります。
パフォーマンスの低下:場合によっては、GILを無効にすると、複数のスレッドを管理し、共有データへのアクセスを同期させるためのオーバーヘッドが増加するため、実際にパフォーマンスが低下する可能性がある。
デバッグの困難性: GILを使用しないマルチスレッド・アプリケーションのデバッグは、手動で確保しなければならないスレッド同期が追加されるため、より複雑になる可能性があります。
デッドロックのリスク: GILがないと、デッドロックが発生しやすくなります。デッドロックとは、2つ以上のスレッドがもう一方のスレッドがリソースを解放するのをいつまでも待ち続けることです。
GILを無効にすることで、特定のCPUバウンドのマルチスレッド・アプリケーションのパフォーマンスが向上する可能性がありますが、それは危険な動きであり、スレッド同期に精通していて、マルチスレッドの複雑さと潜在的な落とし穴を管理する準備ができていない限り、通常は推奨されません。
GILを無効にする以外の方法はありますか?
はい、グローバルインタープリタロック(GIL)を無効にしなくても、Pythonで並行処理や並列処理を扱う方法は他にもいくつかあります:
マルチスレッド: マルチスレッド:I/Oバインドが大きいタスク(複数のWebリクエスト、ファイルやデータベースからの読み込みなど)には、Pythonのスレッドモジュールを使って並行処理を実現できます。GILはCPUバウンドタスクのボトルネックになりますが、I/OバウンドタスクはGILを解放し、I/O待ちのときに他のスレッドを実行させることができます。
マルチプロセッシング: Pythonのマルチプロセッシング・モジュールは、Pythonインタプリタ・プロセスを個別に作成することでGILを回避し、CPUバウンドタスクの真の並列処理を可能にします。これはプロセス間通信のオーバーヘッドを伴い、より多くのメモリを使用する可能性がありますが、GILを回避する堅牢な方法です。
非同期プログラミング: Pythonのasyncioモジュールや、TwistedやTornadoのような非同期ライブラリを使えば、イベントループを使うことで、複数のI/Oバウンドタスクをより効率的に処理できる。このモデルは、I/Oバウンドタスクに固有の待ち時間をフルに活用して他のタスクを実行し、プログラムのスループットを向上させることができます。
数値処理のためのNumPy/SciPy: これらのライブラリは、計算の多くをコンパイルされたコードに変換し、GILを回避することができるため、数値計算に適しており、ベクトル化された演算に最適化されている。
CythonまたはCの拡張: パフォーマンスが重要な場合は、C拡張を書いたり、Cythonを使ってコードの一部をCレベルで書くことができる。ネイティブのCコードでは、GILを解放することができるので、その欠点を緩和することができる。
並列コンピューティング・ライブラリ: joblib、Dask、Rayなどのツールは、複数のプロセッサを使用し、メモリ共有を効率的に管理できる並列計算を実行するための高レベルの環境を提供します。
最適な選択肢は、特定のケースや解決しようとしている問題のタイプによって異なります。各オプションのトレードオフを理解し、要件に最適な選択をすることが常に重要です。
PubNubはどのようにお役に立てるでしょうか?
この記事はPubNub.comに掲載されたものです。
PubNubのプラットフォームは、開発者がWebアプリ、モバイルアプリ、IoTデバイス向けにリアルタイムのインタラクティブ機能を構築、提供、管理できるように支援します。
私たちのプラットフォームの基盤は、業界最大かつ最もスケーラブルなリアルタイムエッジメッセージングネットワークです。世界15か所以上で8億人の月間アクティブユーザーをサポートし、99.999%の信頼性を誇るため、停電や同時実行数の制限、トラフィックの急増による遅延の問題を心配する必要はありません。
PubNubを体験
ライブツアーをチェックして、5分以内にすべてのPubNub搭載アプリの背後にある本質的な概念を理解する
セットアップ
PubNubアカウントにサインアップすると、PubNubキーに無料ですぐにアクセスできます。
始める
PubNubのドキュメントは、ユースケースやSDKに関係なく、あなたを立ち上げ、実行することができます。