web-dev-qa-db-ja.com

concurrent.futuresからのProcessPoolExecutorは、multiprocessing.Poolよりもはるかに遅い

Python 3.2で導入された新しい光沢のある concurrent.futures モジュールを試してみましたが、ほとんど同じコードで、並行からプールを使用していることに気付きました。 futuresは multiprocessing.Pool を使用するよりもway遅くなります。

これはマルチプロセッシングを使用するバージョンです:

def hard_work(n):
    # Real hard work here
    pass

if __name__ == '__main__':
    from multiprocessing import Pool, cpu_count

    try:
        workers = cpu_count()
    except NotImplementedError:
        workers = 1
    pool = Pool(processes=workers)
    result = pool.map(hard_work, range(100, 1000000))

そして、これはconcurrent.futuresを使用しています:

def hard_work(n):
    # Real hard work here
    pass

if __name__ == '__main__':
    from concurrent.futures import ProcessPoolExecutor, wait
    from multiprocessing import cpu_count
    try:
        workers = cpu_count()
    except NotImplementedError:
        workers = 1
    pool = ProcessPoolExecutor(max_workers=workers)
    result = pool.map(hard_work, range(100, 1000000))

この Eli Benderskyの記事 から取られた単純な因数分解関数を使用すると、これらは私のコンピューター(i7、64ビット、Arch Linux)での結果です。

[juanlu@nebulae]─[~/Development/Python/test]
└[10:31:10] $ time python pool_multiprocessing.py 

real    0m10.330s
user    1m13.430s
sys 0m0.260s
[juanlu@nebulae]─[~/Development/Python/test]
└[10:31:29] $ time python pool_futures.py 

real    4m3.939s
user    6m33.297s
sys 0m54.853s

Pythonプロファイラーでこれらをプロファイリングできません。ピクルスエラーが発生するためです。何かアイデアはありますか?

40
astrojuanlu

concurrent.futuresからmapを使用する場合、反復可能 (別々に送信される からの各要素がexecutorに送信され、 Future が作成されます各呼び出しのオブジェクト。次に、futuresによって返された結果を生成するイテレータを返します。
Future オブジェクトはかなり重いです。オブジェクトは、提供するすべての機能(コールバック、キャンセル、ステータスのチェックなど)を許可するために多くの作業を行います。 )。

それに比べて、multiprocessing.Poolはオーバーヘッドがはるかに少ないです。ジョブをバッチで送信し(IPCオーバーヘッドを削減)、関数から返された結果を直接使用します。ジョブの大きなバッチの場合、マルチプロセッシングは間違いなく優れたオプションです。

オーバーヘッドがそれほど重要ではない長時間実行中のジョブを合計したい場合、コールバックで通知したり、ジョブが完了したかどうかを確認したり、個別に実行をキャンセルしたりできるようにしたい場合、Futureは優れています。

個人メモ

Executor.mapを使用する理由はあまり考えられません-タイムアウトを指定する機能を除いて、futureの機能はありません。結果だけに関心がある場合は、multiprocessing.Poolのマップ関数のいずれかを使用するほうがよいでしょう。

55
mata