Pythonでサブプロセス、マルチプロセッシング、スレッドを決定しますか?


110

実行しているマシンで複数のプロセッサを利用できるように、Pythonプログラムを並列化したいと思います。私の並列化は非常に単純です。プログラムのすべての並列「スレッド」は独立しており、それらの出力を別々のファイルに書き込みます。情報を交換するためにスレッドは必要ありませんが、パイプラインの一部のステップは出力に依存しているため、スレッドがいつ終了するかを知ることが不可欠です。

移植性は重要です。Mac、Linux、WindowsのすべてのPythonバージョンで実行したいのです。これらの制約を考えると、これを実装するのに最も適切なPythonモジュールはどれですか。スレッド、サブプロセス、マルチプロセッシングのいずれかを決定しようとしています。これらはすべて、関連する機能を提供しているようです。

これについて何か考えはありますか?ポータブルで最も簡単なソリューションが欲しいです。


関連:stackoverflow.com/questions/1743293/…(スレッドが純粋なPythonコードのスターターではない理由を確認するには、そこで私の回答を読んでください)

1
「すべてのPythonバージョン」は曖昧すぎます。Python 2.3?1.x?3.x?それは単に満たすことが不可能な条件です。
11

回答:


64

multiprocessingスイスアーミーナイフタイプの素晴らしいモジュールです。リモート計算も実行できるため、スレッドよりも一般的です。したがって、これは私が使用することをお勧めするモジュールです。

このsubprocessモジュールを使用すると、複数のプロセスを起動することもできますが、新しいマルチプロセッシングモジュールよりも使い勝手が悪いことがわかりました。

スレッドは悪名高いことで悪名高く、CPythonでは、多くの場合、スレッドは1つのコアに制限されます(ただし、コメントの1つに記載されているように、Pythonコードから呼び出されるCコードでグローバルインタープリターロック(GIL)を解放できます)。 。

引用した3つのモジュールのほとんどの機能は、プラットフォームに依存しない方法で使用できると思います。移植性の面でmultiprocessingは、Python 2.6以降の標準でのみ提供されることに注意してください(ただし、一部の古いバージョンのPython用のバージョンが存在します)。しかし、それは素晴らしいモジュールです!


1
割り当てには、「multiprocessing」モジュールとそのpool.map()メソッドを使用しました。ケーキ!
kmonsoor 2014

セロリのようなものも検討中ですか?それはなぜですか?
user3245268

私が知る限り、Celeryはより複雑です(メッセージブローカーをインストールする必要があります)が、これは、当面の問題に応じて、おそらく検討する必要があるオプションです。
Eric O Lebigot

186

私にとって、これは実際にはかなり単純です:

サブプロセスのオプション:

subprocessある他の実行ファイルを実行するために、それは周りの基本的ラッパーだ--- os.fork()os.execve()オプションの配管のためのいくつかのサポートを(にし、サブプロセスからパイプを設定する。もちろん、あなた可能性のある他のプロセス間通信(IPC)などのソケットなどのメカニズム、またはPOSIXまたはSysV共有メモリですが、呼び出すプログラムがサポートするインターフェイスとIPCチャネルに制限されます。

一般的に、subprocess同期的に使用します-外部ユーティリティを呼び出して、その出力を読み取るか、その完了を待機します(おそらく、一時ファイルから結果を読み取るか、データベースに結果をポストした後)。

ただし、何百ものサブプロセスを生成してポーリングすることができます。私自身のお気に入りのユーティリティクラスはまさにそれを行います。 最大の欠点subprocessモジュールは、I / Oのサポートは、一般的にブロックしていることです。これを修正するためのドラフトPEP-3145がPython 3.xの将来のバージョンと代替のasyncprocにあります(これは、あらゆる種類のドキュメントやREADMEではなく、ダウンロードにつながる警告です)。またfcntlPopenPIPEファイル記述子を直接インポートして直接操作することは比較的簡単です。ただし、これがUNIX以外のプラットフォームに移植可能かどうかはわかりません。

(更新:2019年8月7日:Python 3によるayncioサブプロセスのサポート:asyncioサブプロセス

subprocess サポートを扱うほとんどないイベントがある ... かかわらず、使用できるsignalモジュールとプレーンな古い学校のUNIX / Linuxの信号を---それがあったように、そっと自分のプロセスを殺します。

マルチプロセッシングオプション:

multiprocessingこれは、既存の(Python)コード内で関数を実行するためのもので、このプロセスファミリー間のより柔軟な通信をサポートします。特に、可能な場合multiprocessingはモジュールのQueueオブジェクトを中心にIPC を構築するのが最善ですが、Eventオブジェクトやその他のさまざまな機能を使用することもできます(その一部は、おそらくmmapサポートが十分なプラットフォームでのサポートを中心に構築されています)。

Pythonのmultiprocessingモジュールは、GPython(グローバルインタープリターロック)にもかかわらずCPythonが複数のCPU /コア間で処理をスケーリングできるようにするの 非常によく似たインターフェースと機能を提供することを目的としていthreadingます。これは、OSカーネルの開発者が行ったすべてのきめ細かなSMPロックおよび一貫性の取り組みを活用します。

スレッドオプション:

threadingあるI / Oが結合しているアプリケーションのかなり狭い範囲について(複数のCPUコアを横切ってスケールする必要はありません)と極めて低いレイテンシから利益およびプロセス対(共有コアメモリで)スレッド切り替えのオーバーヘッド切替/コンテキストの切り替え。Linuxでは、これはほとんど空のセットです(Linuxプロセスの切り替え時間は、スレッドの切り替えに非常に近いです)。

threadingPythonに2つの大きな欠点があります。

もちろん、1つは実装固有です---ほとんどがCPythonに影響します。それがGILです。ほとんどの場合、ほとんどのCPythonプログラムは3つ以上のCPU(コア)の可用性の恩恵を受けず、多くの場合、パフォーマンスはGILロック競合の影響を受けます。

実装固有ではない大きな問題は、スレッドが同じメモリ、シグナルハンドラー、ファイル記述子、および特定の他のOSリソースを共有することです。したがって、プログラマーは、オブジェクトのロック、例外処理、および微妙であり、プロセス全体(スレッドのスイート)を強制終了、停止、またはデッドロックする可能性のあるコードの他の側面に非常に注意する必要があります。

比較すると、multiprocessingモデルは各プロセスに独自のメモリ、ファイル記述子などを提供します。それらのいずれかでのクラッシュまたは未処理の例外はそのリソースのみを殺し、子プロセスまたは兄弟プロセスの消失のロバストな処理はデバッグよりもはるかに簡単です。スレッドの同様の問題を修正または回避します。

  • (注:NumPythreadingなどの主要なPythonシステムでの使用は、ほとんどの独自のPythonコードよりもGILの競合による影響がかなり少ない可能性があります。これは、それらがそうするように特別に設計されているためです; NumPyのネイティブ/バイナリ部分、たとえば、それが安全なときにGILを解放します)。

ツイストオプション:

Twistedエレガントで理解するのが非常に難しいもう1つの代替手段を提供していることも注目に値します。基本的に、Twistedのファンがピッチフォークやトーチで家に押し寄せるほど単純化しすぎるリスクがあるため、Twistedは、任意の(単一の)プロセス内でイベント駆動型の協調マルチタスクを提供します。

これがどのように可能かを理解するには、select()select()またはpoll()または同様のOSシステムコールを中心に構築できる)の機能について読む必要があります。基本的には、ファイル記述子のリストでのアクティビティまたはタイムアウトが発生するまで、OSがスリープするように要求する機能によって、すべて駆動されます。

これらの各呼び出しからの目覚めselect()はイベントです---いくつかのソケットまたはファイル記述子で利用可能な(読み取り可能)入力を含むもの、または他の(書き込み可能な)記述子またはソケットで利用可能になるバッファリングスペース、いくつかの例外条件(TCPアウトオブバンドPUSHされたパケットなど)、またはTIMEOUT。

したがって、Twistedプログラミングモデルは、これらのイベントの処理を中心に構築され、結果の「メイン」ハンドラーでループし、ハンドラーにイベントをディスパッチできるようにします。

個人的には、Twistedという名前をプログラミングモデルを思い起こさせるものだと思います。問題へのアプローチは、ある意味で「ねじれ」ている必要があるためです。プログラムを入力データと出力または結果に対する一連の操作として考えるのではなく、プログラムをサービスまたはデーモンとして記述し、さまざまなイベントへの反応を定義します。(実際、Twistedプログラムのコアとなる「メインループ」は(通常は?常に?)aですreactor())。

Twistedを使用する上で主な課題は、イベント駆動モデルに心をゆがめ、Twistedフレームワーク内で連携するように作成されていないクラスライブラリやツールキットの使用を避けることです。Twistedが、SSHプロトコル処理、curses、および独自のサブプロセス/ Popen関数用の独自のモジュール、および最初は赤面したように、Python標準ライブラリの内容を複製しているように見える他の多くのモジュールおよびプロトコルハンドラーを提供するのはこのためです。

Twistedを使用するつもりがない場合でも、概念的なレベルで理解することは有用だと思います。スレッド化、マルチプロセッシング、さらにはサブプロセス処理、さらにはあなたが着手する分散処理におけるパフォーマンス、競合、イベント処理についての洞察を与えるかもしれません。

注: Python 3.xの新しいバージョンには、async def@ async.coroutineデコレーター、awaitキーワードなどのasyncio(非同期I / O)機能が含まれており、将来のサポートから得られます。これらはすべておおまかに似ていますプロセス(協調マルチタスク)の観点からねじれた)。(Python 3のTwistedサポートの現在のステータスについては、https//twistedmatrix.com/documents/current/core/howto/python3.htmlを確認してください

分散オプション:

まだ尋ねていないが、検討する価値のある処理のもう1つの領域は、分散処理の領域です。分散処理と並列計算のための多くのPythonツールとフレームワークがあります。個人的には、最も簡単に使用できるのは、そのスペースにあるとはあまり考えられないものだと思います。

Redisの周りに分散処理を構築するのはほとんど簡単です。キーストア全体を使用して、作業単位と結果を保存できます。RedisLISTをQueue()同様のオブジェクトとして使用でき、PUB / SUBサポートをと同様のEvent処理に使用できます。キーをハッシュして値を使用し、Redisインスタンスの緩いクラスター全体に複製して、トポロジーとハッシュトークンマッピングを保存して、ワーカーを調整するための単一インスタンスの容量を超えてスケ​​ーリングするための一貫したハッシュとフェイルオーバーを提供できます。それらの間でデータ(ピクル化、JSON、BSON、またはYAML)をマーシャリングします。

もちろん、あなたが再実装し、すでに、使用して解決されている機能の多くはRedisの周りに大規模かつより洗練されたソリューションを構築するために開始するようセロリApacheのスパークHadoopの飼育係がetcdカサンドラのように。これらはすべて、Pythonがサービスにアクセスするためのモジュールを備えています。

[更新:分散システム全体で計算集中型のPythonを検討している場合に考慮すべきいくつかのリソース:IPython ParallelPySpark。これらは汎用の分散コンピューティングシステムですが、特にアクセスしやすく、人気のあるサブシステムのデータサイエンスとアナリティクスです]。

結論

シングルスレッドから、サブプロセスへの単純な同期呼び出し、ポーリングされたサブプロセスのプール、スレッド化およびマルチプロセッシング、イベント駆動型の協調マルチタスク、そして分散処理に至るまで、Pythonのさまざまな処理選択肢があります。


1
ただし、classes / OOPでマルチプロセッシングを使用することは困難です。
Tjorriemorrie、2015

2
@Tjorriemorrie:他のプロセスにある可能性のあるオブジェクトのインスタンスへのメソッド呼び出しをディスパッチするのは難しいと思います。これはスレッドで発生する問題と同じですが、(壊れやすく、あいまいな競合状態に陥るのではなく)すぐに見えるようにすることをお勧めします。推奨されるアプローチは、シングルスレッド、マルチスレッド、およびプロセス間で機能するQueueオブジェクトを介してこのようなディスパッチが発生するように調整することだと思います。(一部のRedisまたはCelery Queue実装では、ノードのクラスター全体でも)
Jim Dennis

2
これは本当に良い答えです。私はそれがPython3ドキュメントの並行処理の概要にあればいいのにと思います。
root-11

1
@ root-11をドキュメント管理者に提案してください。ここで無料で公開しています。あなたと彼らはそれを全部または部分的に使用することを歓迎します。
ジム・デニス

「私にとって、これは実際にはかなり単純です:」大好きです。どうもありがとう
ジェローム

5

同様のケースで、私は個別のプロセスと、ネットワークソケットを介した必要な通信の少しを選択しました。移植性が高く、Pythonを使用して実行するのは非常に簡単ですが、おそらくもっと簡単ではありません(私の場合、別の制約もありました:C ++で書かれた他のプロセスとの通信)。

あなたの場合、少なくともCPythonを使用するときのPythonスレッドは実際のスレッドではないので、私はおそらくマルチプロセスに行くでしょう。まあ、それらはネイティブシステムスレッドですが、Pythonから呼び出されたCモジュールはGILを解放する場合としない場合があり、ブロッキングコードを呼び出すときに他のスレッドが実行できるようにします。


4

CPythonで複数のプロセッサを使用するには、モジュールしか選択できませんmultiprocessing。CPythonはその内部(GIL)のロックを保持し、他のCPUのスレッドが並行して動作するのを防ぎます。multiprocessingモジュールは、(のような新しいプロセスを作成しますsubprocess)とそれらの間の通信を管理します。


5
それは真実ではありません。AFAIKではC APIを使用してGILを解放できます。IronPythonやJythonなど、Pythonの他の実装には、このような制限の影響を受けません。私は反対票を投じなかった。
BastienLéonard10年

1

シェルアウトして、あなたの仕事をするためにunixを出してください:

iterpipesを使用してサブプロセスをラップし、次のようにします。

テッド・ジウバのサイトから

INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM並列プロセス

または

Gnu Parallelも役立ちます

あなたはあなたのマルチコア仕事をするために密室の男の子を送り出す間、あなたはGILとたむろします。


6
「Mac、Linux、WindowsのすべてのPythonバージョンでこれを実行したいので、移植性は重要です。」
11

このソリューションでは、ジョブと繰り返しやり取りできますか?あなたはマルチプロセッシングでこれを行うことができますが、サブプロセスではそうは思いません。
abalter
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.