サブロウ丸

Sabrou-mal サブロウ丸

主にプログラミングと数学

Python multiprocessingで並列処理

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


参考: