pythonの標準モジュールmultiprocessing
を使用することで、簡単にプロセス並列処理を行うことができます。
Pool
map
基本的な使い方は
from multiprocessing import Pool with Pool(processes=None) as pool: pool.map(func, iter)
Pool
の引数であるprocessesには並列処理に使用するプロセス数を指定します。
デフォルト値であるNoneを渡すと, processesにはos.cpu_count()
(現在の環境でのcpu数)が代入されます。
func
にはイテレータiter
の要素を引数とする関数を指定します。
(引数の個数が複数ある場合はmap
ではなくstartmap
を使用します。(後述))
pool.map(func, iter[, chunksize])
でイテレータの各要素を引数として関数を並列で実行します。
例えば、
from multiprocessing import Pool def hoge(x): print(x + 'が、おいしい') fruits = ['りんご', 'ぶどう', 'なし', 'みかん', 'かき'] with Pool(processes=2) as pool: pool.map(hope, fruits)
のようなコードを実行すると、
ぶどうが、おいしい りんごが、おいしい なしが、おいしい みかんが、おいしい かきが、おいしい
という、出力を得ます。
chunksizeについて
並列処理はmap
に入力したイテレータからchunksizeの長さ分だけジョブが切り出されて各プロセスに渡されます。
プロセスが渡されたジョブを完了すると、また次のジョブがイテレータから切り出されてプロセスに渡される仕組みです。
デフォルトでは、chunksizeは使用するプロセス数*4となっています。
例えば関数の引数によって処理の時間にばらつきがあり, 重い処理の引数がある程度存在する場合は、
特に重たい処理となる引数をイテレータの序盤に持ってくるのに加え、
chunksizeを小さい整数(例えば1)に設定する方が効率よくcpuを使える(i.e. ジョブ全体が終了するまでに、休むプロセス数が減る)と思うのですが、どうでしょうか。。(つまり、あるプロセスが、実行の後半に処理時間が長い引数を受け取ることを避けることができる)
(上の例でいうと)
with Pool(processes=2) as pool: pool.map(hope, fruits, 1) # pool.map(func=hope, iter=fruits, chunksize=1)
とすれば、chunksizeを設定できます。
starmap
starmap
を使えば関数の引数を増やすことができます。
from multiprocessing import Pool def hoge(x, y): print('{}は{}よりもおいしい'.format(x, y)) fruits = [('りんご', 'いちじく'), ('ぶどう', 'いちご'), ('なし', 'スイカ')] with Pool(processes=2) as pool: pool.starmap(hoge, fruits)
りんごはいちじくよりもおいしい なしはスイカよりもおいしい ぶどうはいちごよりもおいしい
追記(2018/01/11)
並列処理を使い, matplotlibにて画像を大量生成しようとしたところ,
objc[9945]: +[NSView initialize] may have been in progress in another thread when fork() was called. objc[9945]: +[NSView initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
なるエラーが吐き出されました。
調べてみると, GUIのbackendを指定しなければならないようです。
import matplolib.pyplot as plt
↓
import matplotlib matplotlib.use('agg') import matplotlib.pyplot as plt
参考: