グローバルインタープリターロックとは何ですか、なぜ問題なのですか?
PythonからのGILの削除に関して多くのノイズが発生していますが、それがなぜそれほど重要なのかを理解したいと思います。私はコンパイラーやインタープリターを自分で書いたことがないので、詳細にこだわる必要はありません。理解してもらう必要があるでしょう。
グローバルインタープリターロックとは何ですか、なぜ問題なのですか?
PythonからのGILの削除に関して多くのノイズが発生していますが、それがなぜそれほど重要なのかを理解したいと思います。私はコンパイラーやインタープリターを自分で書いたことがないので、詳細にこだわる必要はありません。理解してもらう必要があるでしょう。
回答:
PythonのGILは、異なるスレッドからインタープリター内部へのアクセスをシリアル化することを目的としています。マルチコアシステムでは、複数のスレッドが複数のコアを効果的に利用できないことを意味します。(GILがこの問題につながらなかった場合、ほとんどの人はGILを気にしません。マルチコアシステムの普及により、GILは問題として取り上げられているだけです。)詳細を知りたい場合は、このビデオを見るか、このスライドのセットを見ることができます。情報が多すぎるかもしれませんが、詳細を尋ねました:-)
PythonのGILは、CPython(参照実装)にとって本当に問題であることに注意してください。JythonとIronPythonにはGILがありません。Python開発者は、C拡張機能を記述しているのでない限り、一般的にGILに出くわすことはありません。C拡張機能の作成者は、拡張機能がブロックI / Oを行うときにGILを解放する必要があります。これにより、Pythonプロセスの他のスレッドが実行される機会が得られます。
regex
、lxml
、numpy
モジュールを。Cythonは、GILをカスタムコードでリリースすることを許可します。例b2a_bin(data)
お互いのデータに実際には触れない複数のスレッドがあるとします。これらは可能な限り独立して実行する必要があります。(たとえば)関数を呼び出すために取得する必要がある「グローバルロック」がある場合、ボトルネックになる可能性があります。そもそも、複数のスレッドを持つことであまりメリットが得られない場合があります。
現実の例えに例えると、コーヒーマグが1つしかない会社で働いている100人の開発者を想像してみてください。ほとんどの開発者は、コーディングする代わりにコーヒーを待つ時間を費やします。
これはPython固有のものではありません。そもそもPythonがGILを必要としていた理由の詳細はわかりません。しかし、うまくいけば、それはあなたに一般的な概念のより良い考えを与えてくれます。
まず、Python GILが提供するものを理解しましょう。
操作/命令はすべてインタプリタで実行されます。GILは、インタプリタが特定の瞬間に単一のスレッドによって保持されることを保証します。そして、複数のスレッドを持つpythonプログラムは、単一のインタープリターで動作します。特定の瞬間に、このインタープリターは単一のスレッドによって保持されます。これは、インタプリタを保持しているスレッドだけがいつでも実行されていることを意味します。
なぜそれが問題なのですか?
あなたのマシンは複数のコア/プロセッサを持っている可能性があります。また、複数のコアにより、複数のスレッドを同時に実行できます。つまり、複数のスレッドが特定の瞬間に実行される可能性があります。。ただし、インタープリターは単一のスレッドによって保持されているため、コアにアクセスできても、他のスレッドは何も実行していません。したがって、現時点ではインタープリターを保持しているスレッドが使用しているコアである単一のコアのみが使用されているため、複数のコアによって提供される利点はありません。したがって、プログラムは、シングルスレッドプログラムであるかのように実行に時間がかかります。
ただし、I / O、画像処理、NumPy数値の計算など、ブロックまたは長時間実行される可能性のある操作は、GILの外部で発生します。ここから撮影。したがって、そのような操作の場合、GILが存在しても、マルチスレッド操作はシングルスレッド操作よりも高速です。したがって、GILが常にボトルネックになるとは限りません。
編集:GILはCPythonの実装の詳細です。IronPythonとJythonにはGILがないため、PyPyとJythonを使用したことがなく、確信が持てないので、真にマルチスレッド化されたプログラムが可能です。
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関数を呼び出します)。
2つのスレッドが同じ変数にアクセスできるときはいつでも問題があります。たとえばC ++では、問題を回避する方法は、2つのスレッドがオブジェクトのセッターに同時に入るのを防ぐために、いくつかのミューテックスロックを定義することです。
Pythonではマルチスレッド化が可能ですが、2つのスレッドを1つのPython命令よりも細かい粒度で同時に実行することはできません。実行中のスレッドは、GILと呼ばれるグローバルロックを取得しています。
つまり、マルチコアプロセッサを利用するためにマルチスレッドコードの記述を開始しても、パフォーマンスは向上しません。通常の回避策は、マルチプロセスに移行することです。
たとえば、Cで記述したメソッドの内部にいる場合は、GILを解放できることに注意してください。
GILの使用はPythonに固有のものではなく、最も一般的なCPythonを含むそのインタープリターの一部に固有のものです。(#edited、コメントを参照)
GILの問題はPython 3000でも引き続き有効です。
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バウンドタスクにのみ適していると結論付けていますが、ProcessPoolExecutor
CPUバウンドタスクも処理できます。
次の質問では、なぜGILが最初から存在するのかを尋ねます。なぜグローバルインタープリターロックなのか?
プロセスとスレッドの実験
で、スレッディングPythonの対マルチプロセッシング私はPythonでスレッド対プロセスの実験的な分析を行ってきました。
結果のクイックプレビュー:
Python(CPythonなど)がGILを使用する理由
http://wiki.python.org/moin/GlobalInterpreterLockから
CPythonでは、グローバルインタープリターロック(GIL)は、複数のネイティブスレッドが一度にPythonバイトコードを実行できないようにするミューテックスです。このロックは、主にCPythonのメモリ管理がスレッドセーフではないために必要です。
Pythonからそれを削除する方法?
Luaのように、おそらくPythonが複数のVMを起動できるかもしれませんが、Pythonはそれを実行しません。他のいくつかの理由があるはずです。
Numpyまたはその他のpython拡張ライブラリでは、GILを他のスレッドにリリースすると、プログラム全体の効率が向上する場合があります。
ビジュアルエフェクトのマルチスレッドの本の例を共有したいと思います。だからここに古典的なデッドロックの状況があります
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 ║
╚═══╩════════════════════════════════════════╩══════════════════════════════════════╝