python multiprocessing の使い方(続) Pool編


pythonでmultiprocessingの使い方を調査しています。
先ほど投稿した記事の調査の続き。別プロセスで動かしたい関数をProcess で一つ一つ起動するのでなく、まとめて実行してくれる関数Pool を利用します。

  • 並列処理で使用するコアの数(上限数)を指定できる。
  • 指定した処理を別コアで動かしてくれる。感じ。
  • 並列処理をしたい関数が同じで引数が違うだけの時、Pool を使って簡単に書ける。

を確認しました。

テストコード

  • CPUの数は cpu_count() で取得できる
  • Pool(num_cpu) で Pool のインスタンスを作り、`map(method, iterable_arg)

from multiprocessing import Pool, cpu_count
from time import sleep
from os import getpid, getppid
from numpy import exp, log

def f(args):
    print("[{}---{}] args {}".format(getpid(), getppid(), args))
    if isinstance(args, dict):
        y = args["x"]
    elif isinstance(args, int):
        y = args
    retValue =0.0
    try:
        sleep(1)
        for i in range(1000000):
            retValue = retValue/float(i+1) + log(exp(y * y + 1) - 0.1)
        print("here=", 100/0.0)
    except Exception as e:
        print("[{}---{}]  {}".format(getpid(), getppid(), e))
        raise e
    finally:
        print('[{}---{}] return: {}'.format(getpid(), getppid(), retValue))
        return(retValue)

if __name__ == "__main__":
    print("main pid={} cpu_count={}".format(getpid(), cpu_count()))
    p = Pool(6) # プロセス数を指定する
    try:
        result = p.map(f, [{"x":1}, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10])  # リストで引数を指定する
        print(result)
    except Exception as e:
        print("main", e)

実行結果

ちゃんとCPUを分けて使ってくれています。8コアのPCですが、Pool(10) としてもエラーは出ません。8個すべてを使って全力で処理してくれているようです。

他に気が付いたこと。基礎知識が無いので実験的に学んでいます。汗;

  • プロセスIDは再利用されている。(ので、関数の実行のunique な識別には利用できませんね。)
  • 発生したException は親プロセスではキャッチされない。(そりゃそうか)
  • mapで実行させる関数の引数は、型が違ってもOK。map はIterable なもので、実行させる関数できちんと処理をしていれば。

またCPU数については、ドキュメントには
multiprocessing.cpu_count() については、APIの説明で

This number is not equivalent to the number of CPUs the current process can use. The number of usable CPUs can be obtained with len(os.sched_getaffinity(0))

と書かれていましたが、私が実行したときは、os.sched_getaffinity(0)はいつも {0, 1, 2, 3, 4, 5, 6, 7} でした。CPUの割り当て方とか何か実装方法があるのかな。

参考にさせていただいた情報

来週、何とかなるといいな。
(2020/04/18)

追記(2020/04/18 17:40)
- よく読むと、Pool を使うときはwith Pool(processes=4) as pool: としたほうが良いみたい。with を抜けるときにPool のprocessが close されるみたい。