2017-07-13 10 views
3

私はpiを計算するためのpythonプログラムを作成しました。私はいくつかのプロセスを走らせるためにmpi4pyを書いてみることにしました。プログラムは動作しますが、元のpythonバージョンとは異なるpi値を返します。私がこの問題をもっと調べた結果、より多くのプロセッサーで実行すると精度の低い値が返されることがわかりました。なぜMPIのバージョンは、より多くのプロセッサで結果を変えるのですか?また、個々のメッセージをたくさん送信するのではなく、ブロードキャストを使用する方が理にかなっていますか?それがより効果的であれば、私はどのようにして放送を実装するでしょうか?MPIプロセッサの量によってエラーが発生し、ブロードキャストを実装する方法は?

MPIバージョン:

#!/apps/moose/miniconda/bin/python 
from mpi4py import MPI 
comm = MPI.COMM_WORLD 
rank = comm.Get_rank() 
size = comm.Get_size() 
name = MPI.Get_processor_name() 
def f(x): 
    return (1-(float(x)**2))**float(0.5) 
n = 1000000 
nm = dict() 
pi = dict() 
for i in range(1,size+1): 
    if i == size: 
     nm[i] = (i*n/size)+1 
    else: 
     nm[i] = i*n/size 
if rank == 0: 
    val = 0 
    for i in range(0,nm[1]): 
     val = val+f(float(i)/float(n)) 
    val = val*2 
    pi[0] = (float(2)/n)*(float(1)+val) 
    print name, "rank", rank, "calculated", pi[0] 
    for i in range(1, size): 
     pi[i] = comm.recv(source=i, tag=i) 
    number = sum(pi.itervalues()) 
    number = "%.20f" %(number) 
    import time 
    time.sleep(0.3) 
    print "Pi is approximately", number 
for proc in range(1, size): 
    if proc == rank: 
     val = 0 
     for i in range(nm[proc]+1,nm[proc+1]): 
      val = val+f(float(i)/float(n)) 
     val = val*2 
     pi[proc] = (float(2)/n)*(float(1)+val) 
     comm.send(pi[proc], dest=0, tag = proc) 
     print name, "rank", rank, "calculated", pi[proc] 

オリジナルPythonのバージョン:

#!/usr/bin/python 
n = 1000000 
def f(x): 
    return (1-(float(x)**2))**float(0.5) 
val = 0 
for i in range(n): 
    i = i+1 
    val = val+f(float(i)/float(n)) 
val = val*2 
pi = (float(2)/n)*(float(1)+val) 
print pi 

答えて

2

あなたのコードは、ディスクの四半期の面積を計算することによってを推定し、それは、台形則を使用してのintergralです。

あなたのコードの問題は、各プロセスのI値の範囲が完了していないということです。確かに、何が起こっているかを確認するために、小さなnと印刷iを使用しています。たとえば、for i in range(nm[proc]+1,nm[proc+1]):for i in range(nm[proc],nm[proc+1]):に変更する必要があります。それ以外の場合、i = nm [proc]は決して処理されません。 はまた、pi[0] = (float(2)/n)*(float(1)+val)pi[proc] = (float(2)/n)*(float(1)+val)において、用語float(1)は、x = 0から一体に来ます。しかし、それは何度もカウントされます。エラーの数はプロセスの数によって直接変化するため、プロセス数を増やすと、報告した症状である精度が低下します。

放送は、コミュニケータのすべてのプロセスが特定のプロセスから同じデータを取得する必要があり、状況に対応しています。逆に、すべてのプロセッサーからのデータを合計して1つのプロセス(「ルート」と呼ばれます)で使用可能な結果を​​生成する必要があります。後者の動作は還元と呼ばれ、comm.Reduce()によって行われます。

ここにはsend()recv()の代わりにcomm.Reduce()を使用しているあなたのコードがあります。

from mpi4py import MPI 
import numpy as np 

comm = MPI.COMM_WORLD 
rank = comm.Get_rank() 
size = comm.Get_size() 
name = MPI.Get_processor_name() 
def f(x): 
    return (1-(float(x)**2))**float(0.5) 

n = 10000000 
nm =np.zeros(size+1,'i') 

nm[0]=1 
for i in range(1,size+1): 
    if i == size: 
     nm[i]=n 
    else: 
     nm[i] = (i*n)/size 

val=0 
for i in range(nm[rank],nm[rank+1]): 
    val = val+f((float(i))/float(n)) 

out=np.array(0.0, 'd') 
vala=np.array(val, 'd') 
comm.Reduce([vala,MPI.DOUBLE],[out,MPI.DOUBLE],op=MPI.SUM,root=0) 
if rank == 0: 
    number =(float(4)/n)*(out)+float(2)/n 
    number = "%.20f" %(number) 
    import time 
    time.sleep(0.3) 
    print "Pi is approximately", number 
+0

コードははるかに正確であり、ありがとうございます。しかし、私はprocesesの数を変えて実行すると、異なる結果を返すことに気付きました。彼らははるかに正確ですが、依然として異なっています。なぜこれが当てはまるのでしょうか? – Paul

+0

1つのプロセスの出力は3.14159265355250161278、2は3.14159265355271211106、3つは3.14159265355250472140 – Paul

+0

私は10000000000の間隔と500プロセッサの大きなクラスタでも実行しました。このテストの結果は3.95590113700216061687となり、積分の間隔をより正確にする必要があります。 – Paul

関連する問題