マルチプロセッシング:クラスで定義された関数でPool.mapを使用する方法


179

私が次のようなものを実行すると:

from multiprocessing import Pool

p = Pool(5)
def f(x):
     return x*x

p.map(f, [1,2,3])

それは正常に動作します。ただし、これをクラスの関数として配置すると、次のようになります。

class calculate(object):
    def run(self):
        def f(x):
            return x*x

        p = Pool()
        return p.map(f, [1,2,3])

cl = calculate()
print cl.run()

次のエラーが表示されます。

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/sw/lib/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/sw/lib/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/sw/lib/python2.6/multiprocessing/pool.py", line 225, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

同じ種類の問題を扱っているAlex Martelliからの投稿を見たことがありますが、それは十分に明確ではありませんでした。


1
「これはクラスの関数として」?実際にエラーが発生するコードを投稿できますか。実際のコードがなければ、私たちはあなたが間違っていることしか推測できません。
S.Lott、2010

一般的な注意として、Pythonの標準ピクルモジュールよりも強力なピクルモジュールが存在します(この回答で言及されているpicloudモジュールのように)。
クラウスse

1
のクロージャーにも同様の問題IPython.Parallelがありましたが、オブジェクトをノードにプッシュすることで問題を回避できました。マルチプロセッシングでこの問題を回避するのはかなり面倒です。
Alex S

ここでcalculate、これは上にコピーすることをコンストラクタと関数オブジェクト作成)1によって解決することができるようですので、pickle化可能なであるcalculate場合、次いで、2)この関数オブジェクトのインスタンスを渡すPoolmap方法。番号?
rd11、2014

1
@math Pythonの「最近の変更」が役に立たないと思う。multiprocessingモジュールのいくつかの制限は、クロスプラットフォームの実装であるという目標fork(2)と、Windows でのようなシステムコールの欠如によるものです。Win32のサポートを気にしない場合は、より単純なプロセスベースの回避策があるかもしれません。あなたの代わりにプロセスのスレッドを使用するように準備している場合は、あなたは置き換えることができfrom multiprocessing import Poolfrom multiprocessing.pool import ThreadPool as Pool
Aya

回答:


69

また、pool.mapが受け入れることができる関数の種類の制限にも悩まされました。これを回避するために以下を書きました。parmapを再帰的に使用しても機能するようです。

from multiprocessing import Process, Pipe
from itertools import izip

def spawn(f):
    def fun(pipe, x):
        pipe.send(f(x))
        pipe.close()
    return fun

def parmap(f, X):
    pipe = [Pipe() for x in X]
    proc = [Process(target=spawn(f), args=(c, x)) for x, (p, c) in izip(X, pipe)]
    [p.start() for p in proc]
    [p.join() for p in proc]
    return [p.recv() for (p, c) in pipe]

if __name__ == '__main__':
    print parmap(lambda x: x**x, range(1, 5))

1
これは私にとって非常にうまくいきました、ありがとう。私は1つの弱点を発見しました。defaultdictを通過するいくつかの関数でparmapを使用してみたところ、PicklingErrorが再度発生しました。私はこれに対する解決策を理解していませんでした。defaultdictを使用しないようにコードを作り直しました。
サンセリフ

2
これは、Python 2.7.2では動作しません(デフォルト、Jun 12 2011、15:08:59)[MSC v.1500 32ビット(Intel)](win32)
ubershmekel

3
これは、Python 2.7.3 Aug 1,2012、05:14:39では機能します。これは巨大なイテラブルでは機能しません-> OSErrorが発生します:[Errno 24]開くパイプの数が原因で開いているファイルが多すぎます。
EiyrioüフォンKauyf

このソリューションは、各作業項目のプロセスを生成します。以下の「クラウスse」の解法はより効率的です。
ypnos 2013

85

「multiprocessing.Pool」を使用するコードはラムダ式で機能せず、「multiprocessing.Pool」を使用しないコードは作業項目と同じ数のプロセスを生成するため、これまでに投稿されたコードを使用できませんでした。

事前定義された数のワーカーを生成し、アイドルワーカーが存在する場合にのみ入力リストを反復処理するようにコードを適合させました。また、ワーカーctrl-cが期待どおりに機能するように、「デーモン」モードを有効にしました。

import multiprocessing


def fun(f, q_in, q_out):
    while True:
        i, x = q_in.get()
        if i is None:
            break
        q_out.put((i, f(x)))


def parmap(f, X, nprocs=multiprocessing.cpu_count()):
    q_in = multiprocessing.Queue(1)
    q_out = multiprocessing.Queue()

    proc = [multiprocessing.Process(target=fun, args=(f, q_in, q_out))
            for _ in range(nprocs)]
    for p in proc:
        p.daemon = True
        p.start()

    sent = [q_in.put((i, x)) for i, x in enumerate(X)]
    [q_in.put((None, None)) for _ in range(nprocs)]
    res = [q_out.get() for _ in range(len(sent))]

    [p.join() for p in proc]

    return [x for i, x in sorted(res)]


if __name__ == '__main__':
    print(parmap(lambda i: i * 2, [1, 2, 3, 4, 6, 7, 8]))

2
どのようにして、このparmap関数を適切に処理するための進行状況バーを取得しますか
ショックバーナー2014

2
質問-このソリューションを使用しましたが、生成したpythonプロセスがメモリ内でアクティブのままであることに気付きました。あなたのパーマップが終了したときにそれらを殺す方法についての簡単な考えはありますか?
CompEcon 2014年

1
@ klaus-seコメントで感謝を言うのはやめられますが、あなたの答えは私にとってあまりにも価値があるので、私は抵抗することができませんでした。私はあなたに1つ以上の評判を与えることができればいいのに...
deshtop

2
@greole (None, None)が最後のアイテムとして渡されるfunことは、各プロセスのアイテムのシーケンスの最後に達したことを示しています。
aganders3 2015

4
@deshtop:十分な評判がある場合は、賞金を受け取ることができます:-)
Mark

57

標準ライブラリの外にジャンプしない限り、マルチプロセッシングとピクルは機能しなくなり、制限されます。

multiprocessingcalledのフォークを使用する場合pathos.multiprocesssing、マルチプロセッシングのmap関数でクラスとクラスメソッドを直接使用できます。これは、あるdillの代わりに使用されているpicklecPickle、およびdillPythonでほとんど何をシリアル化することができます。

pathos.multiprocessing非同期のマップ関数も提供します…そして、それはmap複数の引数(例えばmap(math.pow, [1,2,3], [4,5,6]))で機能することができます

ディスカッションを参照してください: マルチプロセッシングとディルは一緒に何ができますか?

および:http : //matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization

変更なしで、インタプリタから最初に記述したコードも処理します。 なぜ、より脆弱で単一のケースに固有の何かをするのですか?

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> class calculate(object):
...  def run(self):
...   def f(x):
...    return x*x
...   p = Pool()
...   return p.map(f, [1,2,3])
... 
>>> cl = calculate()
>>> print cl.run()
[1, 4, 9]

ここでコードを取得:https : //github.com/uqfoundation/pathos

そして、それができることのもう少しを自慢して見せるために:

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> 
>>> p = Pool(4)
>>> 
>>> def add(x,y):
...   return x+y
... 
>>> x = [0,1,2,3]
>>> y = [4,5,6,7]
>>> 
>>> p.map(add, x, y)
[4, 6, 8, 10]
>>> 
>>> class Test(object):
...   def plus(self, x, y): 
...     return x+y
... 
>>> t = Test()
>>> 
>>> p.map(Test.plus, [t]*4, x, y)
[4, 6, 8, 10]
>>> 
>>> res = p.amap(t.plus, x, y)
>>> res.get()
[4, 6, 8, 10]

1
pathos.multiprocessingにamapは、プログレスバーやその他の非同期プログラミングの使用を可能にする非同期マップ()もあります。
マイクマッカーンズ2014

私はpathos.multiprocessingが好きです。これは、マルチプロセッシングを楽しみながら、非並列マップのほぼドロップインの置換を提供できます。複数のコアにまたがる読み取り専用の大きなデータ構造を処理するときに、よりメモリ効率が高くなるように、pathos.multiprocessing.mapの単純なラッパーがありますこのgitリポジトリを参照してください。
Fashandge 2014

興味深いようですが、インストールされません。:これはメッセージピップ与えているCould not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
xApple

1
はい。機能を個別のパッケージに分割し、2/3互換コードに変換しているため、しばらくリリースしていません。上記の多くはモジュール化されmultiprocessており、2/3互換です。stackoverflow.com/questions/27873093/…およびpypi.python.org/pypi/multiprocessを参照してください。
マイクマッカーンズ2016年

3
@xApple:フォローアップと同様pathosに、新しい安定版リリースがあり、2.xおよび3.x互換です。
Mike McKerns

40

私の知る限り、現在のところ、問題の解決策はありmap()ません。与える関数には、モジュールのインポートを通じてアクセスできる必要があります。これが、ロバートのコードが機能f()する理由です。この関数は、次のコードをインポートすることで取得できます。

def f(x):
    return x*x

class Calculate(object):
    def run(self):
        p = Pool()
        return p.map(f, [1,2,3])

if __name__ == '__main__':
    cl = Calculate()
    print cl.run()

これはWindowsプラットフォームの推奨事項に準拠しているため、「メイン」セクションを実際に追加しました(「メインモジュールが、意図しない副作用を引き起こすことなく、新しいPythonインタープリターによって安全にインポートできることを確認してください」)。

また、PEP 8Calculate準拠するために、の前に大文字を追加しました。:)


18

mruleによる解決策は正しいですが、バグがあります。子が大量のデータを送り返すと、親が子のpipe.send()終了を待っている間に、パイプのバッファーがいっぱいになり、子のがブロックされる可能性がありpipe.join()ます。解決策は、子にアクセスする前に子のデータを読み取るjoin()ことです。さらに、デッドロックを防ぐために、子は親のパイプの端を閉じる必要があります。以下のコードはそれを修正します。これparmapにより、の要素ごとに1つのプロセスが作成されることにも注意してくださいX。より高度なソリューションは、を使用していくつかのチャンクmultiprocessing.cpu_count()に分割Xし、結果をマージしてから戻ることです。mruleによる良い答えの簡潔さを損なうことがないように、それは読者への課題として残しておきます。;)

from multiprocessing import Process, Pipe
from itertools import izip

def spawn(f):
    def fun(ppipe, cpipe,x):
        ppipe.close()
        cpipe.send(f(x))
        cpipe.close()
    return fun

def parmap(f,X):
    pipe=[Pipe() for x in X]
    proc=[Process(target=spawn(f),args=(p,c,x)) for x,(p,c) in izip(X,pipe)]
    [p.start() for p in proc]
    ret = [p.recv() for (p,c) in pipe]
    [p.join() for p in proc]
    return ret

if __name__ == '__main__':
    print parmap(lambda x:x**x,range(1,5))

プロセスの数はどのように選択しますか?
patapouf_ai

しかし、それはエラーのためにかなり速く死にますOSError: [Errno 24] Too many open files。プロセスが適切に機能するには、プロセス数に何らかの制限が必要だと思います...
patapouf_ai

13

私もこれで苦労しました。簡単な例として、クラスのデータメンバーとして関数を使用しました。

from multiprocessing import Pool
import itertools
pool = Pool()
class Example(object):
    def __init__(self, my_add): 
        self.f = my_add  
    def add_lists(self, list1, list2):
        # Needed to do something like this (the following line won't work)
        return pool.map(self.f,list1,list2)  

同じクラス内からのPool.map()呼び出しで関数self.fを使用する必要があり、self.fはタプルを引数として取りませんでした。この関数はクラスに埋め込まれているため、他の回答が提案するラッパーのタイプをどのように記述するかはわかりませんでした。

最初の要素が関数であり、残りの要素がeval_func_tuple(f_args)と呼ばれるその関数への引数であるタプル/リストを取得する別のラッパーを使用して、この問題を解決しました。これを使用して、問題のある行をreturn pool.map(eval_func_tuple、itertools.izip(itertools.repeat(self.f)、list1、list2))で置き換えることができます。ここに完全なコードがあります:

ファイル:util.py

def add(a, b): return a+b

def eval_func_tuple(f_args):
    """Takes a tuple of a function and args, evaluates and returns result"""
    return f_args[0](*f_args[1:])  

ファイル:main.py

from multiprocessing import Pool
import itertools
import util  

pool = Pool()
class Example(object):
    def __init__(self, my_add): 
        self.f = my_add  
    def add_lists(self, list1, list2):
        # The following line will now work
        return pool.map(util.eval_func_tuple, 
            itertools.izip(itertools.repeat(self.f), list1, list2)) 

if __name__ == '__main__':
    myExample = Example(util.add)
    list1 = [1, 2, 3]
    list2 = [10, 20, 30]
    print myExample.add_lists(list1, list2)  

main.pyを実行すると[11、22、33]が得られます。これを自由に改善してください。たとえば、eval_func_tupleは、キーワード引数を取るように変更することもできます。

別の注記では、別の回答では、「parmap」関数は、使用可能なCPUの数よりも多くのプロセスの場合により効率的にすることができます。以下の編集済みバージョンをコピーしています。これは私の最初の投稿で、元の回答を直接編集する必要があるかどうかわかりませんでした。一部の変数の名前も変更しました。

from multiprocessing import Process, Pipe  
from itertools import izip  

def spawn(f):  
    def fun(pipe,x):  
        pipe.send(f(x))  
        pipe.close()  
    return fun  

def parmap(f,X):  
    pipe=[Pipe() for x in X]  
    processes=[Process(target=spawn(f),args=(c,x)) for x,(p,c) in izip(X,pipe)]  
    numProcesses = len(processes)  
    processNum = 0  
    outputList = []  
    while processNum < numProcesses:  
        endProcessNum = min(processNum+multiprocessing.cpu_count(), numProcesses)  
        for proc in processes[processNum:endProcessNum]:  
            proc.start()  
        for proc in processes[processNum:endProcessNum]:  
            proc.join()  
        for proc,c in pipe[processNum:endProcessNum]:  
            outputList.append(proc.recv())  
        processNum = endProcessNum  
    return outputList    

if __name__ == '__main__':  
    print parmap(lambda x:x**x,range(1,5))         

8

私はklaus seの回答とaganders3の回答を取り、より読みやすく1つのファイルに保持される文書化されたモジュールを作成しました。プロジェクトに追加するだけです。オプションのプログレスバーもあります!

"""
The ``processes`` module provides some convenience functions
for using parallel processes in python.

Adapted from http://stackoverflow.com/a/16071616/287297

Example usage:

    print prll_map(lambda i: i * 2, [1, 2, 3, 4, 6, 7, 8], 32, verbose=True)

Comments:

"It spawns a predefined amount of workers and only iterates through the input list
 if there exists an idle worker. I also enabled the "daemon" mode for the workers so
 that KeyboardInterupt works as expected."

Pitfalls: all the stdouts are sent back to the parent stdout, intertwined.

Alternatively, use this fork of multiprocessing: 
https://github.com/uqfoundation/multiprocess
"""

# Modules #
import multiprocessing
from tqdm import tqdm

################################################################################
def apply_function(func_to_apply, queue_in, queue_out):
    while not queue_in.empty():
        num, obj = queue_in.get()
        queue_out.put((num, func_to_apply(obj)))

################################################################################
def prll_map(func_to_apply, items, cpus=None, verbose=False):
    # Number of processes to use #
    if cpus is None: cpus = min(multiprocessing.cpu_count(), 32)
    # Create queues #
    q_in  = multiprocessing.Queue()
    q_out = multiprocessing.Queue()
    # Process list #
    new_proc  = lambda t,a: multiprocessing.Process(target=t, args=a)
    processes = [new_proc(apply_function, (func_to_apply, q_in, q_out)) for x in range(cpus)]
    # Put all the items (objects) in the queue #
    sent = [q_in.put((i, x)) for i, x in enumerate(items)]
    # Start them all #
    for proc in processes:
        proc.daemon = True
        proc.start()
    # Display progress bar or not #
    if verbose:
        results = [q_out.get() for x in tqdm(range(len(sent)))]
    else:
        results = [q_out.get() for x in range(len(sent))]
    # Wait for them to finish #
    for proc in processes: proc.join()
    # Return results #
    return [x for i, x in sorted(results)]

################################################################################
def test():
    def slow_square(x):
        import time
        time.sleep(2)
        return x**2
    objs    = range(20)
    squares = prll_map(slow_square, objs, 4, verbose=True)
    print "Result: %s" % squares

編集:@ alexander-mcfarlaneの提案とテスト関数を追加しました


プログレスバーの1つの問題...このバーは、ワークロードがプロセッサー間でどの程度非効率的に分割されたかを測定するだけです。ワークロードが完全に分割されている場合、すべてのプロセッサーがjoin()同時に実行さ100%れ、tqdmディスプレイに完了のフラッシュが表示されます。各プロセッサが偏っワークロードを持っているならば、それは有用であろう唯一の時間がある
アレキサンダー・マクファーレン

1
tqdm()行をラップするように移動してください:result = [q_out.get() for _ in tqdm(sent)]そして、それははるかにうまく機能します-これは本当に感謝していますが、大きな努力です+1
Alexander McFarlane

そのアドバイスをありがとう、私はそれを試してから答えを更新します!
xApple、2016

答えが更新され、プログレスバーの動作が大幅に改善されました。
xApple

8

これは6年以上前に尋ねられたことは知っていますが、上記の提案のいくつかはひどく複雑に思われるため、私のソリューションを追加したかっただけですが、私のソリューションは実際には非常に単純でした。

私がしなければならなかったのは、ヘルパー関数へのpool.map()呼び出しをラップすることだけでした。メソッドの引数と共にクラスオブジェクトをタプルとして渡すと、次のようになります。

def run_in_parallel(args):
    return args[0].method(args[1])

myclass = MyClass()
method_args = [1,2,3,4,5,6]
args_map = [ (myclass, arg) for arg in method_args ]
pool = Pool()
pool.map(run_in_parallel, args_map)

7

クラス内で定義された関数(クラス内の関数内であっても)は、実際にはピクルしません。ただし、これは機能します:

def f(x):
    return x*x

class calculate(object):
    def run(self):
        p = Pool()
    return p.map(f, [1,2,3])

cl = calculate()
print cl.run()

15
おかげで、クラスの外で関数を定義するのは少し汚いことがわかりました。クラスは、特定のタスクを達成するために必要なすべてをバンドルする必要があります。
Mermoz 2010

3
@メモ:「クラスは必要なものすべてをバンドルする必要があります」本当に?私はこれの多くの例を見つけることができません。ほとんどのクラスは他のクラスまたは関数に依存しています。クラスの依存関係を「ダーティ」と呼ぶのはなぜですか?依存関係の何が問題になっていますか?
S.Lott、

まあ、関数は既存のクラスデータを変更するべきではありません-他のプロセスのバージョンを変更するため-それは静的メソッドである可能性があります。静的メソッドを一種のピクルスにすることができます:stackoverflow.com/questions/1914261/… または、この些細なことには、ラムダを使用できます。
ロバート

6

私はこの質問が8年と10か月前に尋ねられたことを知っていますが、私の解決策を紹介したいと思います。

from multiprocessing import Pool

class Test:

    def __init__(self):
        self.main()

    @staticmethod
    def methodForMultiprocessing(x):
        print(x*x)

    def main(self):
        if __name__ == "__main__":
            p = Pool()
            p.map(Test.methodForMultiprocessing, list(range(1, 11)))
            p.close()

TestObject = Test()

クラス関数を静的メソッドにするだけです。しかし、それはクラスメソッドでも可能です:

from multiprocessing import Pool

class Test:

    def __init__(self):
        self.main()

    @classmethod
    def methodForMultiprocessing(cls, x):
        print(x*x)

    def main(self):
        if __name__ == "__main__":
            p = Pool()
            p.map(Test.methodForMultiprocessing, list(range(1, 11)))
            p.close()

TestObject = Test()

Python 3.7.3でテスト済み


3

クラウスセの方法を変更しました。これは、小さなリストで機能しているときに、アイテムの数が1000以上になるとハングするためです。ジョブをNone停止条件で一度に1つずつプッシュするのではなく、入力キューを一度にすべてロードし、プロセスが空になるまでプロセスに割り当てます。

from multiprocessing import cpu_count, Queue, Process

def apply_func(f, q_in, q_out):
    while not q_in.empty():
        i, x = q_in.get()
        q_out.put((i, f(x)))

# map a function using a pool of processes
def parmap(f, X, nprocs = cpu_count()):
    q_in, q_out   = Queue(), Queue()
    proc = [Process(target=apply_func, args=(f, q_in, q_out)) for _ in range(nprocs)]
    sent = [q_in.put((i, x)) for i, x in enumerate(X)]
    [p.start() for p in proc]
    res = [q_out.get() for _ in sent]
    [p.join() for p in proc]

    return [x for i,x in sorted(res)]

編集:残念ながら、現在、システムでこのエラーが発生しています:マルチプロセッシングキューの最大サイズの制限は32767です。うまくいけば、そこでの回避策が役立つでしょう。


1

エラーが示すようにできPoolないため、クラス内のオブジェクトのリストからオブジェクトを手動で無視すると、問題なくコードを実行できますpickle。これは、以下の__getstate__関数(ここも参照)を使用して行うことができます。Poolオブジェクトが見つけようとします__getstate____setstate__機能を、あなたが実行したときにそれを見つけた場合、それらを実行しmapmap_asyncなど。

class calculate(object):
    def __init__(self):
        self.p = Pool()
    def __getstate__(self):
        self_dict = self.__dict__.copy()
        del self_dict['p']
        return self_dict
    def __setstate__(self, state):
        self.__dict__.update(state)

    def f(self, x):
        return x*x
    def run(self):
        return self.p.map(self.f, [1,2,3])

次に行います:

cl = calculate()
cl.run()

出力が得られます:

[1, 4, 9]

上記のコードをPython 3.xでテストしましたが、動作します。


0

このアプローチが採用されているかどうかはわかりませんが、私が使用している回避策は次のとおりです。

from multiprocessing import Pool

t = None

def run(n):
    return t.f(n)

class Test(object):
    def __init__(self, number):
        self.number = number

    def f(self, x):
        print x * self.number

    def pool(self):
        pool = Pool(2)
        pool.map(run, range(10))

if __name__ == '__main__':
    t = Test(9)
    t.pool()
    pool = Pool(2)
    pool.map(run, range(10))

出力は次のようになります。

0
9
18
27
36
45
54
63
72
81
0
9
18
27
36
45
54
63
72
81

0
class Calculate(object):
  # Your instance method to be executed
  def f(self, x, y):
    return x*y

if __name__ == '__main__':
  inp_list = [1,2,3]
  y = 2
  cal_obj = Calculate()
  pool = Pool(2)
  results = pool.map(lambda x: cal_obj.f(x, y), inp_list)

クラスの異なるインスタンスごとにこの関数を適用する可能性があります。次に、これも解決策です

class Calculate(object):
  # Your instance method to be executed
  def __init__(self, x):
    self.x = x

  def f(self, y):
    return self.x*y

if __name__ == '__main__':
  inp_list = [Calculate(i) for i in range(3)]
  y = 2
  pool = Pool(2)
  results = pool.map(lambda x: x.f(y), inp_list)

0

ここに私の解決策がありますが、ここでは他のほとんどの人よりもハックが少ないと思います。夜更かしの答えに似ています。

someclasses = [MyClass(), MyClass(), MyClass()]

def method_caller(some_object, some_method='the method'):
    return getattr(some_object, some_method)()

othermethod = partial(method_caller, some_method='othermethod')

with Pool(6) as pool:
    result = pool.map(othermethod, someclasses)

0

http://www.rueckstiess.net/research/snippets/show/ca1d7d90およびhttp://qingkaikong.blogspot.com/2016/12/python-parallel-method-in-class.htmlから

外部関数を作成して、クラスselfオブジェクトをシードできます。

from joblib import Parallel, delayed
def unwrap_self(arg, **kwarg):
    return square_class.square_int(*arg, **kwarg)

class square_class:
    def square_int(self, i):
        return i * i

    def run(self, num):
        results = []
        results = Parallel(n_jobs= -1, backend="threading")\
            (delayed(unwrap_self)(i) for i in zip([self]*len(num), num))
        print(results)

またはjoblibなし:

from multiprocessing import Pool
import time

def unwrap_self_f(arg, **kwarg):
    return C.f(*arg, **kwarg)

class C:
    def f(self, name):
        print 'hello %s,'%name
        time.sleep(5)
        print 'nice to meet you.'

    def run(self):
        pool = Pool(processes=2)
        names = ('frank', 'justin', 'osi', 'thomas')
        pool.map(unwrap_self_f, zip([self]*len(names), names))

if __name__ == '__main__':
    c = C()
    c.run()

0

これはあまり良い解決策ではないかもしれませんが、私の場合は、このように解決します。

from multiprocessing import Pool

def foo1(data):
    self = data.get('slf')
    lst = data.get('lst')
    return sum(lst) + self.foo2()

class Foo(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def foo2(self):
        return self.a**self.b   

    def foo(self):
        p = Pool(5)
        lst = [1, 2, 3]
        result = p.map(foo1, (dict(slf=self, lst=lst),))
        return result

if __name__ == '__main__':
    print(Foo(2, 4).foo())

self関数を介してクラスの属性と関数にアクセスする必要があるため、関数に渡す必要がありました。これは私のために働いています。訂正や提案はいつでも歓迎します。


0

以下は、python3でマルチプロセッシングプールを使用するために書いたボイラープレートです。具体的には、テストの実行にpython3.7.7が使用されました。私はを使って最速の走行を得ましたimap_unordered。シナリオを接続して試してください。timeitまたはを使用time.time()して、どれが最適かを判断できます。

import multiprocessing
import time

NUMBER_OF_PROCESSES = multiprocessing.cpu_count()
MP_FUNCTION = 'starmap'  # 'imap_unordered' or 'starmap' or 'apply_async'

def process_chunk(a_chunk):
    print(f"processig mp chunk {a_chunk}")
    return a_chunk


map_jobs = [1, 2, 3, 4]

result_sum = 0

s = time.time()
if MP_FUNCTION == 'imap_unordered':
    pool = multiprocessing.Pool(processes=NUMBER_OF_PROCESSES)
    for i in pool.imap_unordered(process_chunk, map_jobs):
        result_sum += i
elif MP_FUNCTION == 'starmap':
    pool = multiprocessing.Pool(processes=NUMBER_OF_PROCESSES)
    try:
        map_jobs = [(i, ) for i in map_jobs]
        result_sum = pool.starmap(process_chunk, map_jobs)
        result_sum = sum(result_sum)
    finally:
        pool.close()
        pool.join()
elif MP_FUNCTION == 'apply_async':
    with multiprocessing.Pool(processes=NUMBER_OF_PROCESSES) as pool:
        result_sum = [pool.apply_async(process_chunk, [i, ]).get() for i in map_jobs]
    result_sum = sum(result_sum)
print(f"result_sum is {result_sum}, took {time.time() - s}s")

上記のシナリオでは、imap_unordered実際には私にとって最悪のパフォーマンスを示しているようです。ケースを試し、実行する予定のマシンでベンチマークしてください。プロセスプールについても読んでください。乾杯!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.