web-dev-qa-db-ja.com

Pythonで並列プログラミングをする方法

C++では、OpenMPを使用して並列プログラミングを実行できます。ただし、OpenMPはPythonでは機能しません。 Pythonプログラムの一部を並列化したい場合はどうすればいいですか?

コードの構造は次のように考えられます。

 solve1(A)
 solve2(B)

solve1solve2は2つの独立した関数です。実行時間を短縮するために、この種のコードを順番にではなく並列に実行する方法誰かが私を助けることができると思います。どうもありがとうございました。コードは次のとおりです。

def solve(Q, G, n):
    i = 0
    tol = 10 ** -4

    while i < 1000:
        inneropt, partition, x = setinner(Q, G, n)
        outeropt = setouter(Q, G, n)

        if (outeropt - inneropt) / (1 + abs(outeropt) + abs(inneropt)) < tol:
            break

        node1 = partition[0]
        node2 = partition[1]

        G = updateGraph(G, node1, node2)

        if i == 999:
            print "Maximum iteration reaches"
    print inneropt

Setinnerとsetouterは2つの独立した機能です。それは私が並行したいところです...

119
ilovecp3

multiprocessing モジュールを使うことができます。この場合、処理プールを使用します。

from multiprocessing import Pool
pool = Pool()
result1 = pool.apply_async(solve1, [A])    # evaluate "solve1(A)" asynchronously
result2 = pool.apply_async(solve2, [B])    # evaluate "solve2(B)" asynchronously
answer1 = result1.get(timeout=10)
answer2 = result2.get(timeout=10)

これはあなたのために一般的な仕事をすることができるプロセスを生み出すでしょう。 processesを渡さなかったので、それはあなたのマシンの各CPUコアに対して一つのプロセスを生み出すでしょう。各CPUコアは同時に1つのプロセスを実行できます。

リストを単一の関数にマップしたい場合は、次のようにします。

args = [A, B]
results = pool.map(solve1, args)

GIL はPythonオブジェクトに対する操作をロックするため、スレッドを使用しないでください。

138
Matt Williamson

これは Ray を使って非常にエレガントに実行できます。

例を並列化するには、@ray.remoteデコレータを使って関数を定義し、次に.remoteを使ってそれらを呼び出す必要があります。

import ray

ray.init()

# Define the functions.

@ray.remote
def solve1(a):
    return 1

@ray.remote
def solve2(b):
    return 2

# Start two tasks in the background.
x_id = solve1.remote(0)
y_id = solve2.remote(1)

# Block until the tasks are done and get the results.
x, y = ray.get([x_id, y_id])

multiprocessing モジュールに比べて、これには多くの利点があります。

  1. 同じコードが、マシンのクラスターだけでなくマルチコアマシンでも実行されます。
  2. プロセスは、 共有メモリとゼロコピーシリアル化 によって効率的にデータを共有します。
  3. エラーメッセージはうまく伝播されます。
  4. これらの関数呼び出しは一緒に構成することができます。

    @ray.remote
    def f(x):
        return x + 1
    
    x_id = f.remote(1)
    y_id = f.remote(x_id)
    z_id = f.remote(y_id)
    ray.get(z_id)  # returns 4
    
  5. リモートで関数を呼び出すことに加えて、クラスは アクター としてリモートでインスタンス化することができます。

Ray は私が開発を手助けしてきたフレームワークです。

12

CPythonはグローバルインタープリタロックを使用しているため、パラレルプログラミングはC++よりも少しおもしろい

このトピックには、いくつかの便利な例と課題の説明があります。

Linuxでタスクセットを使用しているマルチコアシステムでのPython Global Interpreter Lock(GIL)回避策?

4
bauman.space

他の人が言っているように、解決策は複数のプロセスを使用することです。しかし、どちらのフレームワークがより適切であるかは、さまざまな要因によって異なります。すでに述べたものに加えて、 charm4pympi4py (私はcharm4pyの開発者です)もあります。

ワーカープールの抽象化を使用するよりも上記の例を実装するためのより効率的な方法があります。メインループは、1000回の反復のそれぞれで同じパラメータ(完全なグラフGを含む)を繰り返しワーカーに送信します。少なくとも1人のワーカーが別のプロセスに常駐するため、これには引数をコピーして他のプロセスに送信する必要があります。これは、オブジェクトのサイズによっては非常にコストがかかる可能性があります。代わりに、労働者に州を保存させ、単に更新された情報を送信させることは意味があります。

例えば、charm4pyではこれは次のようにすることができます。

class Worker(Chare):

    def __init__(self, Q, G, n):
        self.G = G
        ...

    def setinner(self, node1, node2):
        self.updateGraph(node1, node2)
        ...


def solve(Q, G, n):
    # create 2 workers, each on a different process, passing the initial state
    worker_a = Chare(Worker, onPE=0, args=[Q, G, n])
    worker_b = Chare(Worker, onPE=1, args=[Q, G, n])
    while i < 1000:
        result_a = worker_a.setinner(node1, node2, ret=True)  # execute setinner on worker A
        result_b = worker_b.setouter(node1, node2, ret=True)  # execute setouter on worker B

        inneropt, partition, x = result_a.get()  # wait for result from worker A
        outeropt = result_b.get()  # wait for result from worker B
        ...

この例では、実際に必要な作業者は1人だけです。メインループは一方の機能を実行し、ワーカーにもう一方の機能を実行させることができます。しかし、私のコードはいくつかのことを説明するのに役立ちます。

  1. ワーカーAはプロセス0で実行されます(メインループと同じ)。 result_a.get()が結果を待ってブロックされている間、ワーカーAは同じプロセスで計算を行います。
  2. 引数は同じプロセス内にあるため(コピーは必要ありません)、参照によってワーカーAに自動的に渡されます。
1
Juan Galvez