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プロファイラーでこれらをプロファイリングできません。ピクルスエラーが発生するためです。何かアイデアはありますか?
concurrent.futures
からmap
を使用する場合、反復可能 (別々に送信される からの各要素がexecutorに送信され、 Future
が作成されます各呼び出しのオブジェクト。次に、futuresによって返された結果を生成するイテレータを返します。Future
オブジェクトはかなり重いです。オブジェクトは、提供するすべての機能(コールバック、キャンセル、ステータスのチェックなど)を許可するために多くの作業を行います。 )。
それに比べて、multiprocessing.Pool
はオーバーヘッドがはるかに少ないです。ジョブをバッチで送信し(IPCオーバーヘッドを削減)、関数から返された結果を直接使用します。ジョブの大きなバッチの場合、マルチプロセッシングは間違いなく優れたオプションです。
オーバーヘッドがそれほど重要ではない長時間実行中のジョブを合計したい場合、コールバックで通知したり、ジョブが完了したかどうかを確認したり、個別に実行をキャンセルしたりできるようにしたい場合、Futureは優れています。
個人メモ:
Executor.map
を使用する理由はあまり考えられません-タイムアウトを指定する機能を除いて、futureの機能はありません。結果だけに関心がある場合は、multiprocessing.Pool
のマップ関数のいずれかを使用するほうがよいでしょう。