21

Python でマルチプロセッシングを実装する簡単な方法は次のとおりです。

from multiprocessing import Pool

def calculate(number):
    return number

if __name__ == '__main__':
    pool = Pool()
    result = pool.map(calculate, range(4))

先物に基づく代替実装は

from concurrent.futures import ProcessPoolExecutor

def calculate(number):
    return number

with ProcessPoolExecutor() as executor:
    result = executor.map(calculate, range(4))

if __name__ == '__main__'両方の選択肢は本質的に同じことを行いますが、顕著な違いの 1 つは、通常の句でコードを保護する必要がないことです。これは、先物の実装がこれを処理するためですか、それとも別の理由がありますか?

multiprocessingより広い意味で、との違いは何concurrent.futuresですか? どちらが優先されるのはいつですか?

編集:ガードはマルチプロセッシングにのみ必要であるという私の最初の仮定if __name__ == '__main__'は間違っていました。どうやら、Windows の両方の実装でこのガードが必要ですが、UNIX システムでは必要ありません。

4

2 に答える 2

25

実際には、if __name__ == "__main__"ガードも で使用する必要がProcessPoolExecutorあります:カバーの下に配置するために使用multiprocessing.Processしているため、(特に Windows で) picklability に関するすべての同じ警告が適用されます。Poolmultiprocessing.Pool

Python に両方の API がある理由を尋ねられたとき、Jesse Noller (Python のコア貢献者) が行った次の声明によると、それProcessPoolExecutorは最終的に を置き換えることを意図していると思います。multiprocessing.Pool

ブライアンと私は、人々が API に慣れてきたときに行う予定の統合に取り組む必要があります。私の最終的な目標は、基本的な multiprocessing.Process/Queue 以外のものを MP から concurrent.* に移動し、そのバックエンドのスレッド化をサポートすることです。

今のところ、は、より単純な (そしてより制限された) API とProcessPoolExecutorまったく同じことをほとんど行っています。multiprocessing.Poolを使用して回避できる場合はProcessPoolExecutor、それを使用してください。長期的には機能強化が得られる可能性が高いと思います。、、、 などのすべてのヘルパーmultiprocessingを使用できることに注意してください。ProcessPoolExecutorLockQueueManagermultiprocessing.Pool

ただし、API と動作にはいくつかの顕著な違いがあります。

  1. の Process がProcessPoolExecutor突然終了するとBrokenProcessPool例外が発生し、プールが作業を行うのを待っているすべての呼び出しが中止され、新しい作業が送信されなくなります。同じことが に発生した場合、multiprocessing.Pool終了したプロセスを静かに置き換えますが、そのプロセスで実行されていた作業は決して完了しないため、呼び出し元のコードが作業の終了を待って永久にハングする可能性があります。

  2. Python 3.6 以前を実行している場合、initializer/のサポートが にinitargsありませんProcessPoolExecutorこれのサポートは 3.7 でのみ追加されました)。

  3. ProcessPoolExecutorforではサポートされていませんmaxtasksperchild

  4. concurrent.futuresバックポートを手動でインストールしない限り、Python 2.7 には存在しません。

  5. この質問によると、Python 3.5 より下で実行している場合は、multiprocessing.Pool.mapより優れていProcessPoolExecutor.mapます。ワークアイテムごとのパフォーマンスの違いは非常に小さいことに注意してください。そのためmap、非常に大きな iterableを使用している場合にのみ、大きなパフォーマンスの違いに気付くでしょう。パフォーマンスの違いの理由は、multiprocessing.Poolmap に渡された iterable をチャンクにバッチ処理してから、そのチャンクをワーカー プロセスに渡すためです。これにより、親と子の間の IPC のオーバーヘッドが削減されます。ProcessPoolExecutoralways (デフォルトでは 3.5 以降) は、イテラブルから一度に 1 つの項目を子に渡します。これにより、IPC オーバーヘッドが増加するため、大きなイテラブルではパフォーマンスが大幅に低下する可能性があります。幸いなことに、この問題は Python 3.5 で修正されています。chunksizeキーワード引数が に追加されましたProcessPoolExecutor.map。これは、大きな iterable を扱っていることがわかっている場合に、より大きなチャンク サイズを指定するために使用できます。詳細については、このバグを参照してください。

于 2014-07-22T19:40:06.270 に答える
3

if __name__ == '__main__':Python シェルではpython <scriptname.py> [options]なく、コマンド プロンプトでスクリプトを呼び出したことを意味します。import <scriptname>

コマンド プロンプトからスクリプトを呼び出すと、__main__メソッドが呼び出されます。2 番目のブロックでは、

with ProcessPoolExecutor() as executor:
    result = executor.map(calculate, range(4))

block は、コマンド プロンプトから呼び出されたか、シェルからインポートされたかに関係なく実行されます。

于 2014-07-22T19:37:58.593 に答える