CPythonのグローバルインタープリターロック(GIL)とは何ですか?


244

グローバルインタープリターロックとは何ですか、なぜ問題なのですか?

PythonからのGILの削除に関して多くのノイズが発生していますが、それがなぜそれほど重要なのかを理解したいと思います。私はコンパイラーやインタープリターを自分で書いたことがないので、詳細にこだわる必要はありません。理解してもらう必要があるでしょう。


3
David BeazleyがGILについて知りたいと思ったことをすべて教えてくれます。
hughdbrown、2009

1
これは、私が以前書いたGILとPythonのスレッド化について話している長い記事です。それはそれで、詳細の公正な量に入る:jessenoller.com/2009/02/01/...
jnoller

GILの効果を示すコードを次に示し
ます。github.com

3
これがGILの最も良い説明だと思います。読んでください。dabeaz.com/python/UnderstandingGIL.pdf
suhao399

realpython.com/python-gil私はこれが便利だと思った
qwr

回答:


220

PythonのGILは、異なるスレッドからインタープリター内部へのアクセスをシリアル化することを目的としています。マルチコアシステムでは、複数のスレッドが複数のコアを効果的に利用できないことを意味します。(GILがこの問題につながらなかった場合、ほとんどの人はGILを気にしません。マルチコアシステムの普及により、GILは問題として取り上げられているだけです。)詳細を知りたい場合は、このビデオを見るか、このスライドのセットを見ることができます。情報が多すぎるかもしれませんが、詳細を尋ねました:-)

PythonのGILは、CPython(参照実装)にとって本当に問題であることに注意してください。JythonとIronPythonにはGILがありません。Python開発者は、C拡張機能を記述しているのでない限り、一般的にGILに出くわすことはありません。C拡張機能の作成者は、拡張機能がブロックI / Oを行うときにGILを解放する必要があります。これにより、Pythonプロセスの他のスレッドが実行される機会が得られます。


46
良い答え-基本的に、PythonのスレッドはI / Oのブロックにのみ適していることを意味します。アプリがプロセッサ使用率の1 CPUコアを超えることはありません
Ana Betts 08

8
「Python開発者は、C拡張を作成しない限り、一般的にGILに出くわすことはありません」-カタツムリのペースで実行されているマルチスレッドコードの原因がGILであることを知らないかもしれませんが、確かにその効果を感じます。それでも、Pythonで32コアサーバーを利用するには、関連するすべてのオーバーヘッドを伴う32のプロセスが必要であることを意味します。
基本的な

6
@PaulBetts:それは真実ではありません。これは、パフォーマンスクリティカルなコードが既にとGIL例えば、放しない可能Cの拡張使用している可能性がありregexlxmlnumpyモジュールを。Cythonは、GILをカスタムコードでリリースすることを許可します。例b2a_bin(data)
jfs '16

5
@Paul Betts:マルチプロセッシングモジュールを使用すると、1 CPUを超えるプロセッサ使用率のコードを取得できます。複数のプロセスを作成することは、複数のスレッドを作成するよりも「重い」ですが、Pythonで本当に並行して作業を行う必要がある場合は、オプションです。
AJNeufeld 2016年

1
@david_adlerはい、まだ事実であり、しばらくの間そうである可能性があります。それでも、Pythonが多くの異なるワークロードで実際に役立つことを止めることはできませんでした。
Vinay Sajip

59

お互いのデータに実際には触れない複数のスレッドがあるとします。これらは可能な限り独立して実行する必要があります。(たとえば)関数を呼び出すために取得する必要がある「グローバルロック」がある場合、ボトルネックになる可能性があります。そもそも、複数のスレッドを持つことであまりメリットが得られない場合があります。

現実の例えに例えると、コーヒーマグが1つしかない会社で働いている100人の開発者を想像してみてください。ほとんどの開発者は、コーディングする代わりにコーヒーを待つ時間を費やします。

これはPython固有のものではありません。そもそもPythonがGILを必要としていた理由の詳細はわかりません。しかし、うまくいけば、それはあなたに一般的な概念のより良い考えを与えてくれます。


コーヒーマグを待つことを除いて、マグカップを待つ間に他のことを確実に行うことができるので、かなりI / Oバウンドのプロセスのように見えます。GILは、とにかく待機する時間のほとんどを費やすI / Oの重いスレッドにはほとんど影響を与えません。
ランチャー、


36

まず、Python GILが提供するものを理解しましょう。

操作/命令はすべてインタプリタで実行されます。GILは、インタプリタが特定の瞬間に単一のスレッドによって保持されることを保証します。そして、複数のスレッドを持つpythonプログラムは、単一のインタープリターで動作します。特定の瞬間に、このインタープリターは単一のスレッドによって保持されます。これは、インタプリタを保持しているスレッドだけがいつでも実行されていることを意味します。

なぜそれが問題なのですか?

あなたのマシンは複数のコア/プロセッサを持っている可能性があります。また、複数のコアにより、複数のスレッドを同時に実行できます。つまり、複数のスレッドが特定の瞬間に実行される可能性があります。。ただし、インタープリターは単一のスレッドによって保持されているため、コアにアクセスできても、他のスレッドは何も実行していません。したがって、現時点ではインタープリターを保持しているスレッドが使用しているコアである単一のコアのみが使用されているため、複数のコアによって提供される利点はありません。したがって、プログラムは、シングルスレッドプログラムであるかのように実行に時間がかかります。

ただし、I / O、画像処理、NumPy数値の計算など、ブロックまたは長時間実行される可能性のある操作は、GILの外部で発生します。ここから撮影。したがって、そのような操作の場合、GILが存在しても、マルチスレッド操作はシングルスレッド操作よりも高速です。したがって、GILが常にボトルネックになるとは限りません。

編集:GILはCPythonの実装の詳細です。IronPythonとJythonにはGILがないため、PyPyとJythonを使用したことがなく、確信が持てないので、真にマルチスレッド化されたプログラムが可能です。


4
:PyPyにはGILがあります。 リファレンスhttp : //doc.pypy.org/en/latest/faq.html#does-pypy-have-a-gil-why。IronpythonとJythonにはGILがありません。
Tasdik Rahman

実際、PyPyにはGILがありますが、IronPythonにはありません。
エマニュエル

@Emmanuel回答を編集してPyPyを削除し、IronPythonを含めました。
Akshar Raaj

17

Pythonは、本当の意味でのマルチスレッド化を許可していません。マルチスレッドパッケージがありますが、マルチスレッドでコードを高速化したい場合は、通常、それを使用することはお勧めできません。Pythonには、Global Interpreter Lock(GIL)と呼ばれる構造があります。

https://www.youtube.com/watch?v=ph374fJqFPE

GILは、一度に実行できる「スレッド」は1つだけであることを確認します。スレッドはGILを取得し、少し作業を行ってから、GILを次のスレッドに渡します。これは非常に迅速に行われるため、人間の目には、スレッドが並列で実行されているように見えるかもしれませんが、実際には同じCPUコアを使用して順番を取っているだけです。このすべてのGILの通過は、実行にオーバーヘッドを追加します。つまり、コードをより高速に実行したい場合、スレッディングパッケージを使用することは多くの場合良い考えではありません。

Pythonのスレッドパッケージを使用する理由があります。いくつかのことを同時に実行したい場合で、効率が問題にならない場合は、まったく問題なく便利です。または、何か(IOのような)を待つ必要があるコードを実行している場合、それは非常に理にかなっています。ただし、スレッドライブラリでは、追加のCPUコアを使用できません。

マルチスレッドは、オペレーティングシステム(マルチプロセッシングを実行すること)、Pythonコードを呼び出す一部の外部アプリケーション(SparkやHadoopなど)、またはPythonコードが呼び出す一部のコード(例:Pythonコードは、高価なマルチスレッド処理を行うC関数を呼び出します)。


15

2つのスレッドが同じ変数にアクセスできるときはいつでも問題があります。たとえばC ++では、問題を回避する方法は、2つのスレッドがオブジェクトのセッターに同時に入るのを防ぐために、いくつかのミューテックスロックを定義することです。

Pythonではマルチスレッド化が可能ですが、2つのスレッドを1つのPython命令よりも細かい粒度で同時に実行することはできません。実行中のスレッドは、GILと呼ばれるグローバルロックを取得しています。

つまり、マルチコアプロセッサを利用するためにマルチスレッドコードの記述を開始しても、パフォーマンスは向上しません。通常の回避策は、マルチプロセスに移行することです。

たとえば、Cで記述したメソッドの内部にいる場合は、GILを解放できることに注意してください。

GILの使用はPythonに固有のものではなく、最も一般的なCPythonを含むそのインタープリターの一部に固有のものです。(#edited、コメントを参照)

GILの問題はPython 3000でも引き続き有効です。


StacklessにはまだGILがあります。Stacklessは(モジュールのように)スレッド化を改善しません-問題を回避するように試みますが、非ブロッキング関数を必要とするプログラミング(コルーチン)の異なる方法を提供します。
jnoller 2009

3.2の新しいGILはどうですか?
new123456

1つだけのスレッドがメモリを更新する場合、問題がない/ミューテックス/セマフォが必要ないことを追加します。@ new123456は、競合を減らし、シングルスレッドのパフォーマンスを損なうことなくスレッドを適切にスケジュールします(それ自体は印象的です)が、それでもグローバルロックです。
基本的な

14

Python 3.7ドキュメント

また、Pythonのthreadingドキュメントから次の引用を強調したいと思います

CPython実装の詳細:CPythonでは、グローバルインタープリターロックが原因で、一度に1つのスレッドのみがPythonコードを実行できます(特定のパフォーマンス指向のライブラリーがこの制限を克服できる場合でも)。アプリケーションでマルチコアマシンの計算リソースをより有効に利用したい場合は、multiprocessingまたはを使用することをお勧めしますconcurrent.futures.ProcessPoolExecutor。ただし、複数のI / Oバウンドタスクを同時に実行する場合は、スレッド化が適切なモデルです。

これは、GILがPythonのスレッド化並列処理がCPUバウンドタスクには不適切であることを示唆している用語集エントリにglobal interpreter lockリンクしています。

一度に1つのスレッドのみがPythonバイトコードを実行することを保証するためにCPythonインタープリターが使用するメカニズム。これにより、同時アクセスに対してオブジェクトモデル(dictなどの重要な組み込み型を含む)が暗黙的に安全になるため、CPythonの実装が簡素化されます。インタープリター全体をロックすると、マルチプロセッサーマシンが提供する並列処理の多くを犠牲にして、インタープリターをマルチスレッド化することが容易になります。

ただし、標準またはサードパーティの一部の拡張モジュールは、圧縮やハッシュなどの計算集中型のタスクを実行するときにGILを解放するように設計されています。また、I / Oを実行すると、GILは常に解放されます。

「フリースレッド」インタープリター(共有データをより細かくロックするインタープリター)を作成する過去の取り組みは、一般的なシングルプロセッサーのケースでパフォーマンスが低下したため、成功していません。このパフォーマンスの問題を克服すると、実装がはるかに複雑になり、維持にコストがかかると考えられています。

この引用は、dictsと変数の割り当てもCPython実装の詳細としてスレッドセーフであることも示唆しています。

次に、パッケージドキュメントは、multiprocessing次のようなインターフェイスを公開しながらプロセスを生成することでGILを克服する方法を説明していますthreading

マルチプロセッシングは、スレッド化モジュールと同様のAPIを使用してプロセスの生成をサポートするパッケージです。マルチプロセッシングパッケージは、ローカルとリモートの同時実行性を提供し、スレッドの代わりにサブプロセスを使用することにより、グローバルインタープリターロックを効果的に回避します。このため、マルチプロセッシングモジュールを使用すると、プログラマは特定のマシンの複数のプロセッサを完全に活用できます。UnixとWindowsの両方で動作します。

そして、それがバックエンドとして使用することconcurrent.futures.ProcessPoolExecutor説明するためのドキュメントmultiprocessing

ProcessPoolExecutorクラスは、プロセスのプールを使用して非同期に呼び出しを実行するExecutorサブクラスです。ProcessPoolExecutorはマルチプロセッシングモジュールを使用します。これにより、グローバルインタープリターロックを回避できますが、ピックル可能なオブジェクトのみを実行して返すこともできます。

プロセスの代わりにスレッドThreadPoolExecutor使用する他の基本クラスと対照的です

ThreadPoolExecutorは、スレッドのプールを使用して呼び出しを非同期に実行するExecutorサブクラスです。

これはThreadPoolExecutor、I / Oバウンドタスクにのみ適していると結論付けていますが、ProcessPoolExecutorCPUバウンドタスクも処理できます。

次の質問では、なぜGILが最初から存在するのかを尋ねます。なぜグローバルインタープリターロックなのか?

プロセスとスレッドの実験

で、スレッディングPythonの対マルチプロセッシング私はPythonでスレッド対プロセスの実験的な分析を行ってきました。

結果のクイックプレビュー:

ここに画像の説明を入力してください


0

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

http://wiki.python.org/moin/GlobalInterpreterLockから

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

Pythonからそれを削除する方法?

Luaのように、おそらくPythonが複数のVMを起動できるかもしれませんが、Pythonはそれを実行しません。他のいくつかの理由があるはずです。

Numpyまたはその他のpython拡張ライブラリでは、GILを他のスレッドにリリースすると、プログラム全体の効率が向上する場合があります。


0

ビジュアルエフェクトのマルチスレッドの本の例を共有したいと思います。だからここに古典的なデッドロックの状況があります

static void MyCallback(const Context &context){
Auto<Lock> lock(GetMyMutexFromContext(context));
...
EvalMyPythonString(str); //A function that takes the GIL
...    
}

次に、デッドロックが発生するシーケンス内のイベントについて考えます。

╔═══╦════════════════════════════════════════╦══════════════════════════════════════╗
    Main Thread                             Other Thread                         
╠═══╬════════════════════════════════════════╬══════════════════════════════════════╣
 1  Python Command acquires GIL             Work started                         
 2  Computation requested                   MyCallback runs and acquires MyMutex 
 3                                          MyCallback now waits for GIL         
 4  MyCallback runs and waits for MyMutex   waiting for GIL                      
╚═══╩════════════════════════════════════════╩══════════════════════════════════════╝
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.