Raspberry Pi スパコン (15) ナンプレ


2023年 06月 01日

今までの時間計測

    	st_time = time.perf_counter()
    	comm.send(pb, dest=1, tag=1)
    	prob = comm.recv(source=1, tag=2)
    	gen_time = time.perf_counter() - st_time

今までは逐次処理になっていたので、これで時間計測ができた。
しかし、本当の並列処理では、次々にパターンをワーカーに送り、送った順番とは関係なく、出来た順番で問題を受取り、次のパターンを送る。そうなると、マスター側で問題生成時間を計測するのは無理で、ワーカー側で計測しないといけない。

ワーカー側での時間計測

ワーカー側で時間計測するのは簡単だ。
まず、現状のworker()を確認してみよう。

def worker():
    
	status = MPI.Status()
   	 
	while( True ):
    	prob = comm.recv(source=0, tag=MPI.ANY_TAG, status=status)
    	if status.Get_tag()!=1:
        	break
    	prob.retrycount = generator.generate(prob.pattern)
    	prob.problem = generator.getProblem()
       	 
    	comm.send(prob, dest=0, tag=2)

実行時間は、 generator.generate(prob.pattern) の実行に掛かった時間で良いので、この1行の直前、直後で現在時刻を計測し、その差を求めれば良い。

そして、実行時間をマスターに伝えなければいけないので、Problemクラスにtimeを追加し、そこ(prob.time)に計算結果を突っ込んでおくと、Problemオブジェクトであるprobがマスターに送られるときにマスターに渡る。

ということで、worker()は以下のようになった。

def worker():
    
	status = MPI.Status()
   	 
	while( True ):
    	prob = comm.recv(source=0, tag=MPI.ANY_TAG, status=status)
    	if status.Get_tag()!=1:
        	break
   	 
    	st_time = time.perf_counter()
    	prob.retrycount = generator.generate(prob.pattern)
    	prob.time = time.perf_counter() - st_time
    	prob.problem = generator.getProblem()

    	comm.send( prob, dest=0, tag=2 )

マスター側

マスター側では時間計測しないので、その部分を削除する。そして、受け取ったProblemオブジェクトprobの中にある時間(prob.time)を時間表示に使う。

	for pb in problems:
   	 
    	# パターンの送信、問題の受信
    	comm.send(pb, dest=1, tag=1)
    	prob = comm.recv(source=1, tag=2)
    	genproblems.append(prob)
    
    	str = f"No.{prob.index}   H {util.countHint(prob.pattern)}"
    	print(str)
    	if dataoutput != None:
        	print(str,file=dataoutput)
    	util.printHintBoard(prob.pattern)
   	 
    	if prob.retrycount >= 0:
        	if dataoutput != None:
            	util.printBoard(prob.problem,dataoutput)
        	print( f"[{prob.retrycount}]  {prob.time:06f} sec" )
        	util.printBoard(prob.problem)
        	successCount += 1
    	else:
        	str = f"FAILURE {-prob.retrycount}"
        	if dataoutput != None:
            	print(str,file=dataoutput)
        	print(str)
        	failureCount += 1
    	print()
   	 
	comm.send("END", dest=1, tag=9)
    
	print( f"length of genproblems = {len(genproblems)}" )
   	 
	exe_time = time.perf_counter() - start_time

	probSize = len(problems)
	totalCount = successCount+failureCount
	print( f"total {totalCount}  failure {failureCount}\n" )
	print( f"Time\t{exe_time:06f} sec\n" ) 

実行

現状では、2台しか動かしていないので、右の2台のRaspberry PiのLANポートのLEDだけが光っている状況である。2台の間での通信しかないので、LEDのチカチカはほぼ同期する。

NP_MPIGEN2.pyは、まだ並列処理にはなっていないのだが、動作確認しておこう。

$ mpiexec -H RP0,RP1 python3 NP_MPIGEN2.py -g data/Pattern500.txt
No.0   H 18
- - - - X - - - -
- - - X - X - - -
X X - - - - X - -
- - X - - X - - -
- X - - - - - X -
- - - X - - X - -
- - X - - - - X X
- - - X - X - - -
- - - - X - - - -
[0]  0.646241 sec
- - - - 1 - - - -
- - - 4 - 9 - - -
8 6 - - - - 7 - -
- - 9 - - 4 - - -
- 3 - - - - - 7 -
- - - 8 - - 6 - -
- - 4 - - - - 1 9
- - - 7 - 6 - - -
- - - - 2 - - - -

   中略

No.499   H 23
- - X X - - - - -
- - X - - X - - -
- - - - - X - X X
- X X X - X - - X
- - - - - - - - -
X - - X - X X X -
X X - X - - - - -
- - - X - - - - -
- - - - - X X - -
[0]  0.056796 sec
- - 7 3 - - - - -
- - 1 - - 9 - - -
- - - - - 8 - 6 4
- 7 9 1 - 2 - - 8
- - - - - - - - -
8 - - 7 - 4 3 1 -
3 5 - 4 - - - - -
- - - 5 - - - - -
- - - - - 7 6 - -

length of genproblems = 500
total 500  failure 0

Time    1124.461174 sec

$

前回、NP_MPIGEN1.pyを実行では 1732秒だったが、今回はNP_MPIGEN2.pyを実行して1124秒だった。しかし、これはプログラムが改善して速くなったのではなく、乱数を使っているため、たまたま問題が速く作れたに過ぎない。

さて、次回は本当の並列処理、複数台のラズパイに問題生成を割り振ることに手を付けよう。