Python 3でのConcurrent.futuresとマルチプロセッシング


148

Python 3.2では、Concurrent Futuresが導入されました。これは、古いスレッド化モジュールとマルチプロセッシングモジュールの高度な組み合わせのようです。

これを古いマルチプロセッシングモジュールよりもCPUバウンドタスクに使用することの利点と欠点は何ですか?

この記事は、作業がはるかに簡単であることを示唆しています。

回答:


145

私はこれconcurrent.futures以上「高度な」とは言いません。これは、基礎となる並列化の仕掛けとして複数のスレッドまたは複数のプロセスを使用するかどうかに関係なく、ほとんど同じように機能する単純なインターフェイスです。

つまり、「より単純なインターフェース」の事実上すべてのインスタンスと同様に、ほぼ同じトレードオフが関係します。学習曲線が浅くなります。これは、主に、学習できるものが非常に少ないためです。ただし、提供されるオプションが少ないため、最終的には、よりリッチなインターフェースではできない方法で不満を感じる可能性があります。

CPUにバインドされたタスクに関する限り、それはあまり意味がないため、あまり意味がありません。CPythonでのCPUにバインドされたタスクの場合、速度を上げるチャンスを得るには、複数のスレッドではなく複数のプロセスが必要です。ただし、速度向上の程度(ある場合)は、ハードウェア、OSの詳細、特に特定のタスクに必要なプロセス間通信の量によって異なります。内部では、すべてのプロセス間並列化の仕掛けは同じOSプリミティブに依存しています。これらを実現するために使用する高レベルAPIは、最終的な速度の主な要因ではありません。

編集:例

あなたが参照した記事に示されている最終的なコードは次のとおりですが、それを機能させるために必要なインポート文を追加しています。

from concurrent.futures import ProcessPoolExecutor
def pool_factorizer_map(nums, nprocs):
    # Let the executor divide the work among processes by using 'map'.
    with ProcessPoolExecutor(max_workers=nprocs) as executor:
        return {num:factors for num, factors in
                                zip(nums,
                                    executor.map(factorize_naive, nums))}

multiprocessing代わりに使用することとまったく同じです:

import multiprocessing as mp
def mp_factorizer_map(nums, nprocs):
    with mp.Pool(nprocs) as pool:
        return {num:factors for num, factors in
                                zip(nums,
                                    pool.map(factorize_naive, nums))}

multiprocessing.Poolオブジェクトをコンテキストマネージャとして使用する機能は、Python 3.3で追加されました。

どちらが使いやすいですか?笑;-)それらは本質的に同じです。

違いの1つは、Poolさまざまな方法をサポートしているため、学習曲線をかなり上に登るまでは、どれほど簡単かを理解できない可能性があることです。

繰り返しますが、これらすべての異なる方法は、長所と短所の両方です。状況によっては柔軟性が必要になる場合があるため、これらは強みです。「できればそれを行うには、たった1つの明確な方法が望ましい」という理由から、これらは弱点です。(可能であれば)のみに固執するプロジェクトconcurrent.futuresは、その最小限のAPIの使用方法に不必要な新奇性がないため、おそらく長期的には維持しやすくなります。


20
「スピードアップを得るチャンスを得るには、複数のスレッドではなく複数のプロセスが必要です」は厳しすぎる。スピードが重要なら・・・ コードはすでにCライブラリを使用している可能性があるため、正規表現、lxml、numpyなどのGILをリリースできます。
jfs

4
@JFSebastian、追加してくれてありがとう-多分「純粋な CPythonで」と言ったほうがいいかもしれませんが、GILについて議論せずにここで真実を説明する短い方法はないと思います。
Tim Peters

2
また、長いIOでの操作では、スレッドが特に便利で十分な場合があることにも言及する価値があります。
kotrfa

9
@TimPeters ProcessPoolExecutor実際には、キャンセルを許可するインスタンスを返す()、発生し例外を確認する()、完了時に呼び出されるコールバックを動的に追加する()よりもPool、実際には多くのオプションがあります。これらの機能は、によって返されるインスタンスでは使用できません。他の方法に起因してより多くのオプションを持っている/ 、、及びでによって露出複数の方法と、インスタンス。ProcessPoolExecutor.submitFuturecancelexceptionadd_done_callbackAsyncResultPool.apply_asyncPoolinitializerinitargsmaxtasksperchildcontextPool.__init__Pool
最大

2
@maxは確かですが、問題はについてPoolではなく、モジュールについてでした。 Poolはの内容のごく一部でありmultiprocessing、ドキュメントのかなり下にあるため、に存在していることを人々が理解するまでには時間がかかりますmultiprocessing。この特定の回答はPool、OPがリンクするすべての記事であり、それcfは「操作がはるかに簡単」であるため、焦点を当てました。この記事で説明した内容は正しくありません。超えて、というcfのはas_completed()、非常に便利です。
Tim Peters、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.