ノード上のシステム V 共有メモリ セグメントを利用する大規模な Fortran/MPI コード ベースがあります。32 個のプロセッサを搭載したファット ノードで実行しますが、NIC は 2 つまたは 4 つしかなく、CPU あたりのメモリは比較的少なくなっています。そのため、各 CPU が (SMP アレイのブロック内で) 計算を実行する共有メモリ セグメントを設定するという考え方です。次に、MPI を使用してノード間通信を処理しますが、これは SMP グループ内のマスターでのみ行われます。この手順はダブルバッファリングされており、うまく機能しています。
問題は、遅延を少し隠すために非同期通信に切り替えることにしたときに発生しました。ノード上の少数の CPU のみが MPI 経由で通信しますが、すべての CPU が受信した配列を (共有メモリ経由で) 参照するため、何らかのバリアを設定しない限り、CPU は通信中の CPU がいつ終了したかを知りません。では、なぜ非同期通信を行うのでしょうか?
理想的で仮説的な解決策は、要求タグを SMP セグメントに配置し、知る必要がある CPU で mpi_request_get_status を実行することです。もちろんリクエストタグは通信中のCPUにしか登録されていないので動作しません!別の提案された可能性は、通信スレッドでスレッドを分岐し、それを使用して mpi_request_get_status をループで実行し、共有メモリ セグメントのフラグ引数を使用して、他のすべてのイメージが見えるようにすることでした。残念ながら、スレッド化ライブラリを使用しないように制約されているため、これもオプションではありません。
私たちが思いついた唯一の実行可能なオプションは機能しているように見えますが、汚いハックのように感じます. 受信バッファーの上限アドレスにあり得ない値を入れます。これにより、mpi_irecv が完了すると、値が変更され、すべての CPU がいつバッファーを安全に使用できるかがわかります。それは大丈夫ですか?MPI 実装がデータを連続して転送することが保証されている場合にのみ、確実に機能するようです。このことは Fortran で記述したため、配列が連続しているため、これはほとんど説得力があるように思えます。アクセスもそうなると思います。
何かご意見は?
ありがとう、ジョリー
これは、私が行っている種類の疑似コード テンプレートです。自宅に参考用のコードがないので、肝心なことを忘れていないことを願っていますが、オフィスに戻ったときに確認します...
pseudo(array_arg1(:,:), array_arg2(:,:)...)
integer, parameter : num_buffers=2
Complex64bit, smp : buffer(:,:,num_buffers)
integer : prev_node, next_node
integer : send_tag(num_buffers), recv_tag(num_buffers)
integer : current, next
integer : num_nodes
boolean : do_comms
boolean, smp : safe(num_buffers)
boolean, smp : calc_complete(num_cores_on_node,num_buffers)
allocate_arrays(...)
work_out_neighbours(prev_node,next_node)
am_i_a_slave(do_comms)
setup_ipc(buffer,...)
setup_ipc(safe,...)
setup_ipc(calc_complete,...)
current = 1
next = mod(current,num_buffers)+1
safe=true
calc_complete=false
work_out_num_nodes_in_ring(num_nodes)
do i=1,num_nodes
if(do_comms)
check_all_tags_and_set_safe_flags(send_tag, recv_tag, safe) # just in case anything else has finished.
check_tags_and_wait_if_need_be(current, send_tag, recv_tag)
safe(current)=true
else
wait_until_true(safe(current))
end if
calc_complete(my_rank,current)=false
calc_complete(my_rank,current)=calculate_stuff(array_arg1,array_arg2..., buffer(current), bounds_on_process)
if(not calc_complete(my_rank,current)) error("fail!")
if(do_comms)
check_all_tags_and_set_safe(send_tag, recv_tag, safe)
check_tags_and_wait_if_need_be(next, send_tag, recv_tag)
recv(prev_node, buffer(next), recv_tag(next))
safe(next)=false
wait_until_true(all(calc_complete(:,current)))
check_tags_and_wait_if_need_be(current, send_tag, recv_tag)
send(next_node, buffer(current), send_tag(current))
safe(current)=false
end if
work_out_new_bounds()
current=next
next=mod(next,num_buffers)+1
end do
end pseudo
したがって、理想的には、通信プロセスの別のスレッドのループで「check_all_tags_and_set_safe_flags」を実行したかったのですが、さらに良いのは、「セーフフラグ」を廃止し、送信/受信へのハンドルをスレーブで利用できるようにしてから、 「wait_until_true(safe(current))」の代わりに、スレーブでの計算の前に「check_tags_and_wait_if_need_be(current, send_tag, recv_tag)」(mpi_wait) を実行できます。