C++では、OpenMPを使用して並列プログラミングを実行できます。ただし、OpenMPはPythonでは機能しません。 Pythonプログラムの一部を並列化したい場合はどうすればいいですか?
コードの構造は次のように考えられます。
solve1(A)
solve2(B)
solve1
とsolve2
は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つの独立した機能です。それは私が並行したいところです...
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オブジェクトに対する操作をロックするため、スレッドを使用しないでください。
これは 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 モジュールに比べて、これには多くの利点があります。
これらの関数呼び出しは一緒に構成することができます。
@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
Ray は私が開発を手助けしてきたフレームワークです。
CPythonはグローバルインタープリタロックを使用しているため、パラレルプログラミングはC++よりも少しおもしろい
このトピックには、いくつかの便利な例と課題の説明があります。
Linuxでタスクセットを使用しているマルチコアシステムでのPython Global Interpreter Lock(GIL)回避策?
他の人が言っているように、解決策は複数のプロセスを使用することです。しかし、どちらのフレームワークがより適切であるかは、さまざまな要因によって異なります。すでに述べたものに加えて、 charm4py と mpi4py (私は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人だけです。メインループは一方の機能を実行し、ワーカーにもう一方の機能を実行させることができます。しかし、私のコードはいくつかのことを説明するのに役立ちます。
result_a.get()
が結果を待ってブロックされている間、ワーカーAは同じプロセスで計算を行います。