子育てしながらエンジニアしたい

現在 2 歳女の子の子育て中エンジニアによる、技術系 + 日常系ブログ。

Python: マルチプロセスで並列処理をさせるには multiprocessing.Pool が超便利

独立した並列処理を高速化したい

ふつう並列処理といえば、GUI と処理を切り離すなどが思い当たります。
GUI と処理を切り離すといっても、相互にメッセージなどをやり取りして協調動作する必要があります。

今回やりたいのは、そういう協調動作を必要としない、独立した並列処理です。
たとえば、大量のファイルをすべて別個に圧縮するという場合、それぞれのファイルは独立に処理することができます。
これはスレッドを分けても意味が無いので、プロセスを分けて処理する必要があります。
こんなときに、Python の multiprocessing.Pool がとても便利だったので記録として残しておきます。

課題

20 個の処理をマルチプロセスで並列処理したいとします。
CPU がクアッドコアであれば、4 個を同時に走らせることができます。
普通に考えると、プロセスの終了状況を監視して、終了したら次のタスクを割り当てるという流れが必要になります。
実際にこういう方法でプログラムを書いていたのですが、これはけっこう面倒でミスをしやすいプログラムでした。

multiprocessing モジュールの Pool クラスは、なんとこれを 1 行でやってくれます。
はじめから知っておきたかった...

サンプルプログラム

1 秒かけて与えられた数字の 2 乗を返すだけのサンプルプログラムです。
数字は range(20) で与えるようにしていますが、ここはリストなどでも大丈夫です。

出力結果は以下のようになります。

param 0 is being processed
param 2 is being processed
param 4 is being processed
param 6 is being processed
param 1 is being processed
param 3 is being processed
param 5 is being processed
param 7 is being processed
param 8 is being processed
param 10 is being processed
param 12 is being processed
param 14 is being processed
param 9 is being processed
param 11 is being processed
param 13 is being processed
param 15 is being processed
param 16 is being processed
param 18 is being processed
param 17 is being processed
param 19 is being processed
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361]

プロセスを並列で走らせ、返り値は順番どおりに返してくれる、ほんとに便利なモジュールです!

注意点としては、Pool.map は range(20) の値を 0 から順番に与えるわけではない、ということです。
出力結果を見ると、0,2,4,6 が最初に来ていることがわかります。
この順番を 1 ずつにしたい!という場合は、Pool.map にもう一つ引数を追加する必要があります。

results = pool.map(parafunc, range(20), 1)

この最後の引数 "1" は chunksize と呼ばれる引数で、まとまりを意味するようです。
これを 1 にすると 0,1,2,3,...、2 だと 0,2,4,6,...、3 なら 0,3,6,9... という順番で値が渡されます。
なお、ここを変えたとしても、渡される結果の順番は同一なので安心です。