5

ノード上のシステム 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) を実行できます。

4

1 に答える 1

5

「...なんらかの障壁を制定しない限り、ではなぜ非同期通信を行うのでしょうか?」

その文は少し混乱しています。非同期通信の目的は、通信と計算をオーバーラップさせることです。通信が行われている間に実際の作業を完了できることを願っています。しかし、これは、最終的に同期する必要がある 2 つのタスクが発生していることを意味しますそのため、最初の通信フェーズの終わりにタスクが 2 番目の計算フェーズ (またはその他) に進む前にブロックする何かが必要です。

この場合、物事を適切に実装するために何をすべきかという問題 (現在は機能しているように見えますが、結果の脆弱性について当然のことながら懸念しています) は、実装の方法によって異なります。スレッドという言葉を使用しますが、(a) sysv 共有メモリ セグメントを使用しています。これは、スレッドがあれば行う必要はありません。(b) スレッド ライブラリを使用しないように制約されているため、おそらく実際には、MPI_Init() などの後にプロセスを fork() しているということですか?

あなたの最善の策はほぼ確実に計算のノード上の分散に OpenMP を使用することであり、おそらくコードを大幅に簡素化するという Hristo に同意します。スレッド化ライブラリを使用しないという制約について詳しく知っておくと役立ちます。

MPI に加えて使用する「独自の」プロセスベースの通信レイヤーを使用する必要を回避する別のアプローチは、ノード上のすべてのプロセスを MPI プロセスにすることですが、いくつかのコミュニケーターを作成します。グローバル通信、およびノー​​ドごとに 1 つの「ローカル」コミュニケーター。実際にノード外通信を行うコミュニケーターの一部となるのは、ノードごとに数個のプロセスだけであり、他のプロセスは共有メモリ セグメントで動作します。その後、オンノード同期の MPI ベースの同期方法 (Wait または Barrier) を使用できます。今後の MPI3 では、ローカル共有メモリ セグメントをこのように使用するためのいくつかの明示的なサポートが実際に提供される予定です。

最後に、本質的には独自のローカルノードのみの IPC 実装を通じて物事をやり続けることに絶対に縛られ、決心している場合 --- すでに SysV 共有メモリ セグメントを使用しているため、SysV セマフォを使用して同期。データが計算の準備ができたときに「フラグ」を立てるために、独自の(ややデリケートな)セマフォのようなメカニズムをすでに使用しています。ここでは、より堅牢な既に作成されたセマフォを使用して、非 MPI プロセスにデータの計算準備が整ったことを知らせることができます (また、同様のメカニズムを使用して、他のプロセスが計算を完了したことを MPI プロセスに知らせます)。

于 2012-05-18T12:27:48.523 に答える