web-dev-qa-db-ja.com

Python 3.2でforループを並列に実行します

重複の可能性:
単純なpythonループを並列化するにはどうすればよいですか?

私はPython(Python 3.2を使用)を使用)にまったく慣れておらず、並列化について質問があります。実行したいforループがあります。 Python 3.2:で「マルチプロセッシング」を使用した並列処理

def computation:    
    global output

    for x in range(i,j):
        localResult = ... #perform some computation as a function of i and j
        output.append(localResult)

合計で、i = 0からj = 100の範囲でこの計算を実行したいと思います。したがって、それぞれが全範囲のサブドメインで関数「計算」を呼び出すいくつかのプロセスを作成したいと思います。これをどのように行うかについてのアイデアはありますか?マルチプロセッシングを使用するよりも良い方法はありますか?

具体的には、領域分割を実行したいのですが、次のコードがあります。

from multiprocessing import Pool

class testModule:

    def __init__(self):
        self

    def computation(self, args):
        start, end = args
        print('start: ', start, ' end: ', end)

testMod = testModule()
length = 100
np=4
p = Pool(processes=np)
p.map(yes tMod.computation, [(length, startPosition, length//np) for startPosition in    range(0, length, length//np)]) 

PicklingErrorに関するエラーメッセージが表示されます。ここで問題になる可能性のあるアイデアはありますか?

14
user1499144

Joblib は、単純な並列ループの目的でマルチプロセッシングをラップアラウンドするように特別に設計されています。マルチプロセッシングに直接取り組む代わりに、それを使用することをお勧めします。

単純なケースは次のようになります。

from joblib import Parallel, delayed
Parallel(n_jobs=2)(delayed(foo)(i**2) for i in range(10))  # n_jobs = number of processes

構文は、理解すれば簡単です。 delayedを使用して関数fooを呼び出し、その引数を次の括弧内に含めるジェネレータ構文を使用しています。

あなたの場合、ジェネレーター構文でforループを書き直すか、別の関数(つまり、「worker」関数)を定義して、単一ループ反復の操作を実行し、それをParallel呼び出しのジェネレーター構文に配置する必要があります。

後者の場合、次のようにします。

Parallel(n_jobs=2)(delayed(foo)(parameters) for x in range(i,j))

ここで、fooは、forループの本体を処理するために定義する関数です。 Parallelはとにかくリストを返すため、リストに追加したくないことに注意してください。

20
blz

この場合、計算を実行してlocalResultを取得するための単純な関数を定義することをお勧めします。

_def getLocalResult(args):
    """ Do whatever you want in this func.  
        The point is that it takes x,i,j and 
        returns localResult
    """
    x,i,j = args  #unpack args
    return doSomething(x,i,j)
_

これで、計算関数で、ワーカーのプールを作成し、ローカルの結果をマップするだけです。

_import multiprocessing
def computation(np=4):
    """ np is number of processes to fork """
    p = multiprocessing.Pool(np)
    output = p.map(getLocalResults, [(x,i,j) for x in range(i,j)] )
    return output
_

グローバルは不要なので、ここでは削除しました(通常、グローバルは不要です)。呼び出しルーチンでは、output.extend(computation(np=4))または同様のことを行う必要があります。

[〜#〜]編集[〜#〜]

コードの「実用的な」例を次に示します。

_from multiprocessing import Pool

def computation(args):
    length, startPosition, npoints = args
    print(args)

length = 100
np=4
p = Pool(processes=np)
p.map(computation, [(startPosition,startPosition+length//np, length//np) for startPosition in  range(0, length, length//np)])
_

関数としてインスタンスメソッドを使用していたため、機能しなかったことに注意してください。マルチプロセッシングは、新しいプロセスを開始し、pickleを介してプロセス間で情報を送信するため、pickle化できるオブジェクトのみを使用できます。とにかくインスタンスメソッドを使用することは実際には意味がないことに注意してください。各プロセスは親のコピーであるため、プロセスで発生する状態の変更は、とにかく親に伝播されません。

5
mgilson