17

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

私は (Python 3.2 を使用して) Python を初めて使用し、並列化に関して質問があります。Python 3.2 で「マルチプロセッシング」を使用して並列に実行したい for ループがあります。

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 に関するエラー メッセージが表示されます。ここで何が問題になる可能性がありますか?

4

2 に答える 2

20

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 ループをジェネレーター構文で書き直すか、別の関数 (つまり、「ワーカー」関数) を定義して、単一のループ反復の操作を実行し、それを Parallel の呼び出しのジェネレーター構文に配置する必要があります。

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

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

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

于 2012-07-24T13:17:24.380 に答える
6

この場合、計算を実行して を取得する単純な関数を定義することをお勧めします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)])

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

于 2012-07-24T13:18:35.147 に答える