PythonがGILで書かれたのはなぜですか?


112

グローバルインタープリターロック(GIL)は、スレッド処理などがPythonでややこしい理由の主な理由としてよく引用されているようです。

プログラマーではないので、なぜそうなるのか手がかりがありません-GILを入れる背後にあるロジックは何ですか?


10
Wikipediaの記事は、と述べ、「GILは、並列処理言語のダイナミズムを持つために支払った価格に大きな障壁となることができます」、と言うことをするために行くようにロックを採用した理由には、」:シングルスレッドプログラムの高速化(すべてのデータ構造のロックを個別に取得または解放する必要はありません)、通常はスレッドセーフではないCライブラリの容易な統合。
ロバートハーベイ

3
@RobertHarvey、ダイナミズムはそれとは何の関係もありません。問題は突然変異です。
dan_waterworth


1
Javaの符号なし数値の欠如のように、自分が何をしているのかわからない人が足元で撃っているのを防ぐことを意図していると感じるのは仕方ありません。残念ながら、誰でもない、彼らがやっているか知っているが本当の恥がある欠損言語を取得するために非常に多くの他の方法でPythonの岩
基本

1
@Basicでは、暗号計算を行うために、Javaでバイト配列を処理するための標準的な方法が必要です(長い間使用していません)。Python(たとえば)には符号付きの数値はありませんが、より良い方法があるため、ビット単位の演算を実行しようとはしません。
ニックT

回答:


105

Pythonには、CPython、IronPython、RPythonなど、いくつかの実装があります。

GILを持っている人もいれば持っていない人もいます。たとえば、CPythonにはGILがあります。

http://en.wikipedia.org/wiki/Global_Interpreter_Lockから

GILを使用してプログラミング言語で記述されたアプリケーションは、各プロセスに独自のインタープリターがあり、独自のGILがあるため、個別のプロセスを使用して完全な並列処理を実現するように設計できます。

GILの利点

  • シングルスレッドプログラムの速度が向上しました。
  • 通常はスレッドセーフではないCライブラリの簡単な統合。

Python(CPythonなど)がGILを使用する理由

CPythonでは、グローバルインタープリターロック(GIL)は、複数のネイティブスレッドがPythonバイトコードを同時に実行することを防ぐミューテックスです。このロックは、主にCPythonのメモリ管理がスレッドセーフではないために必要です。

GILは、特定の状況でマルチスレッドCPythonプログラムがマルチプロセッサシステムを最大限に活用できないため、物議を醸しています。I / O、画像処理、NumPyの数値計算など、潜在的にブロックまたは長時間実行される操作は、GILの外部で発生することに注意してください。したがって、GILがボトルネックになるのは、CPythonバイトコードを解釈してGIL内で多くの時間を費やすマルチスレッドプログラムのみです。

Pythonには、いくつかの理由により、きめの細かいロックではなくGILがあります。

  • シングルスレッドの場合は高速です。

  • I / Oバウンドプログラムのマルチスレッドの場合は高速です。

  • Cライブラリで計算集中型の作業を行うCPUにバインドされたプログラムのマルチスレッドの場合は高速です。

  • これにより、C拡張機能の記述が簡単になります。Pythonスレッドの切り替えは、許可する場所(Py_BEGIN_ALLOW_THREADSとPy_END_ALLOW_THREADSマクロの間)以外では行われません。

  • Cライブラリのラッピングが簡単になります。スレッドの安全性について心配する必要はありません。ライブラリがスレッドセーフでない場合は、呼び出し中にGILをロックしたままにします。

GILはC拡張によってリリースできます。Pythonの標準ライブラリは、ブロッキングI / O呼び出しごとにGILをリリースします。したがって、GILは、I / Oバウンドサーバーのパフォーマンスに影響を与えません。したがって、プロセス(フォーク)、スレッド、または非同期I / Oを使用して、Pythonでネットワークサーバーを作成できます。GILは邪魔になりません。

CまたはFortranの数値ライブラリは、GILがリリースされると同様に呼び出すことができます。C拡張がFFTの完了を待っている間、インタープリターは他のPythonスレッドを実行します。したがって、この場合もGILは、きめの細かいロックよりも簡単で高速です。これは、数値作業の大部分を構成します。NumPy拡張機能は、可能な限りGILをリリースします。

スレッドは通常、ほとんどのサーバープログラムを作成するのに悪い方法です。負荷が低い場合、分岐は簡単です。負荷が高い場合、非同期I / Oおよびイベント駆動型プログラミング(PythonのTwistedフレームワークを使用するなど)の方が優れています。スレッドを使用する唯一の言い訳は、Windowsにos.forkがないことです。

GILは、純粋なPythonでCPUを集中的に使用する場合にのみ問題になります。ここでは、プロセスとメッセージ受け渡し(mpi4pyなど)を使用して、よりクリーンなデザインを取得できます。Pythonチーズショップには「処理」モジュールもあり、プロセスにスレッドと同じインターフェイスを提供します(つまり、threading.Threadをprocessing.Processに置き換えます)。

スレッドを使用して、GILに関係なくGUIの応答性を維持できます。GILがパフォーマンスを低下させる場合(上記の説明を参照)、スレッドにプロセスを生成させ、それが完了するのを待つことができます。


52
酸っぱいブドウのように聞こえます。Pythonはスレッドを適切に実行できないため、スレッドが不要であるか、さらには悪い理由を構成します。「負荷が低い場合、分岐は簡単です」、真剣に?GILは、参照カウントGCの使用を主張する場合にのみ、これらすべてのケースで「高速」になります。
マイケルボルグワード

9
s/RPython/PyPy/g。@MichaelBorgwardtプロのGILに理由を与えることは、一種の問題のポイントですね。ただし、この回答の内容(つまり、代替案の議論)の一部は重要ではないことに同意します。そして、好むと好まざるとにかかわらず、リカウントは取り除くことはほぼ不可能です。これは、APIとコードベース全体に深く浸透しています。コードの半分を書き直し、すべての外部コードを壊さずにそれを取り除くことはほとんど不可能です。

10
multiprocessingライブラリを忘れないでください-2.6 以降の標準。ワーカープールは、いくつかの単純なタイプの並列処理のための非常に洗練された抽象化です。
ショーンマクサムシング

8
@alcalde自分が何をしているのかわからない場合、および/またはスレッドが協調的/通信できるようにしたくない場合のみ。それ以外の場合、特に一部のOSで新しいプロセスを起動するオーバーヘッドを考慮すると、それは裏面の王室の痛みです。32コアのサーバーがあるため、CPythonでそれらを完全に利用するには32プロセスが必要です。これは「良い解決策」ではなく、CPythonの不備を回避するためのハックです。
基本的な

8
スレッドがWindows以外のプラットフォームに存在するという事実は、フォークがすべての状況で適切ではないことを十分に証明するはずです。
zneak

42

まず、PythonにはGILがありません。Pythonはプログラミング言語です。プログラミング言語は、抽象的な数学的規則と制限のセットです。Python Language Specificationには、GILが存在する必要があるという記述はありません。

Pythonにはさまざまな実装があります。GILを持っている人もいれば、持っていない人もいます。

GILを使用する簡単な説明の1つは、並行コードの記述は難しいということです。コードの周りに巨大なロックを配置することで、常に連続して実行するように強制します。問題が解決しました!

特にCPythonでの重要な目標の1つは、Cで記述されたプラグインでインタープリターを簡単に拡張できるようにすることです。繰り返しますが、並行コードの記述は難しいため、並行性がないことを保証することで、拡張機能の記述が容易になります通訳者。さらに、これらの拡張機能の多くは、並行性を念頭に置いて作成されていない可能性がある既存のライブラリの単なる薄いラッパーです。


6
これはJavaの符号なし数値型の欠如と同じ議論です-開発者は他の誰もが彼らよりも愚かだと思います...-
基本的な

1
@Basic-信じられないかもしれませんが、本当に本当に馬鹿げていなくても、動作させるために特定のことを考えないという単純な仮定を立てる言語を持っていることは依然として有用であることがわかります事。CPythonは、GILを最適なソリューションにした設計上の決定により、これらのアプリケーションのプログラミングが容易になるため、単純なマルチスレッドアプリケーション(プログラムがIOバウンドであり、多くの場合、GILは重要ではない)を含む特定のものに最適です、それがサポートしていること、特に事実のコレクションにアトミック操作を
ジュール

@Julesはい、これらの機能が必要になるまで非常に便利です。「c ++のような別の言語で記述してください」というcpythonの「推奨」ソリューションは、Pythonの単一の利点をすべて失うことを意味します。コードの半分をC ++で書いているのに、なぜPythonから始めるのですか?確かに、小さなAPI / glueプロジェクトの場合は迅速かつ簡単であり、ETLの場合は誰にも負けませんが、重い物を持ち上げる必要があるものには適していません。Javaを使用してハードウェアと通信するのと同じ...飛び越えなければならないフープはほとんど滑comicです。
基本的な

16

GILの目的は何ですか?

CAPIドキュメントには、この件に関して次のように記載されています。

Pythonインタープリターは完全にスレッドセーフではありません。マルチスレッドPythonプログラムをサポートするために、グローバルインタープリターロックまたはGILと呼ばれるグローバルロックがあります。これは、Pythonオブジェクトに安全にアクセスする前に現在のスレッドが保持する必要があります。ロックがなければ、最も単純な操作でもマルチスレッドプログラムで問題が発生する可能性があります。たとえば、2つのスレッドが同じオブジェクトの参照カウントを同時にインクリメントすると、参照カウントは2回ではなく1回だけインクリメントされる可能性があります。

つまり、GILは状態の破損を防ぎます。Pythonプログラムは、メモリセーフな操作のみが許可されているため、セグメンテーションフォールトを生成しないでください。GILは、この保証をマルチスレッドプログラムに拡張します。

代替手段は何ですか?

GILの目的が破損から状態を保護することである場合、1つの明白な代替策は、よりきめ細かいロックです。おそらくオブジェクトごとのレベルで。これに伴う問題は、マルチスレッドプログラムのパフォーマンスを向上させることが実証されていますが、オーバーヘッドが増加し、結果としてシングルスレッドプログラムが苦しむことです。


2
細粒度ロックのgilを置き換えるインタープリターオプションを使用してプログラムをユーザーに実行させ、現在のプロセスがgilを使用してまたは使用せずに発生したかどうかを何らかの方法で(読み取り専用で)知ることは素晴らしいことです。
ルイスMasuelli 14

GILにもかかわらず、モジュールpyodbcの不注意な使用により、マルチスレッドプログラムでセグメンテーションエラーが発生しました。したがって、「セグメンテーション違反が発生することはありません」というのは誤りです。
ムポサット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.