Help us understand the problem. What is going on with this article?

fortran+MPIコーディング入門(3) 1対1通信

前回は多対多の集団通信について学びました。ですが、実際「となりのプロセスにだけデータを送りたい」というような場合もよくあります。今回はそういう場合に使う1対1通信について学びます。

目次(予定)

  1. 並列処理とは
  2. プロセス並列の基本
  3. 集団通信
  4. 1対1通信 ←今ココ
  5. オブジェクト指向コードの並列化

1対1通信

MPI_sendとMPI_recv

文字通りデータを送ります。

ブロッキング通信なので、通信が正常に終了しなければ終了しません。

サンプル

このサンプルでは、右隣に自身のランクを送信し、左隣からランクを受信します。

subroutine test_01()
    integer :: n_data
    integer :: dest     ! 送り先
    integer :: origin   ! 受信データの送信元

    integer :: buf                  ! 受け取るデータ
    integer,allocatable :: ista(:)  ! ステータス格納用

    allocate(ista(mpi_status_size))

    write(LOGUNIT,*) "test 01"

    buf = me
    n_data = 1

    if(me == nnn-1) then
       dest = 0
    else
       dest = me+1
    end if

    if(me == 0) then
       origin = nnn-1
    else
       origin = me-1
    end if

    write(LOGUNIT,*) "origin,dest=", origin,dest

    call mpi_send(buf, n_data, mpi_integer , dest, &
         me, mpi_comm_world, ierr)
    call mpi_recv(buf, n_data, mpi_integer, origin, &
         origin, mpi_comm_world,ista,ierr)

    write(LOGUNIT,*) "buf:", buf

  end subroutine test_01

MPI_sendrecv

sendとrecvは基本的にセットで呼ばれるので、両方まとめたものがこれです。

subroutine test_02()
    integer :: n_data
    integer :: dest
    integer :: origin

    integer :: buf
    integer,allocatable :: ista(:) 

    allocate(ista(mpi_status_size))

    write(LOGUNIT,*) "test 02"

    buf = me
    n_data = 1

    if(me == nnn-1) then
       dest = 0
    else
       dest = me+1
    end if

    if(me == 0) then
       origin = nnn-1
    else
       origin = me-1
    end if

    write(LOGUNIT,*) "origin,dest=", origin,dest

    call mpi_sendrecv(me, n_data, mpi_integer , dest, me,&
         buf, n_data,mpi_integer, origin, origin, & 
         mpi_comm_world, ista, ierr)

    write(LOGUNIT,*) "buf:", buf

  end subroutine test_02

MPI_IsendとMPI_Irecv

mpi_sendとmpi_recvはブロッキング通信ですが、ブロッキングでないものがmpi_isendとmpi_irecvです。

送受信を完了するにはMPI_waitを使う必要があります。

subroutine test_03()
    integer :: n_data
    integer :: dest
    integer :: origin

    integer :: buf
    integer :: ireq
    integer,allocatable :: ista(:) 

    allocate(ista(mpi_status_size))

    write(LOGUNIT,*) "test 03"

    buf = me
    n_data = 1

    if(me == nnn-1) then
       dest = 0
    else
       dest = me+1
    end if

    if(me == 0) then
       origin = nnn-1
    else
       origin = me-1
    end if

    write(LOGUNIT,*) "origin,dest=", origin,dest

    call mpi_isend(me, n_data, mpi_integer , dest, &
         me, mpi_comm_world, ireq, ierr)
    call mpi_irecv(buf, n_data, mpi_integer, origin, &
         origin, mpi_comm_world,ireq,ierr)

    call mpi_wait(ireq, ista, ierr)

    write(LOGUNIT,*) "buf:", buf

  end subroutine test_03

デッドロック

集団通信と異なり、1対1通信では厄介な問題が生じることがあります。それはデッドロックです。

一般にデッドロックとは、複数の処理同士が互いの処理の終了を待っている状態になり、処理が完全に停止してしまうことを言います。排他制御などをかじったことがある人なら知っているかも知れません。

MPIでは次のようにしてデッドロックが生じます。以下のコードはデッドロックを起こし得ます。

! n_dataはかなり大きい
call mpi_recv(buf_recv, n_data, mpi_double_precision, dest, tag, mpi_comm_world, ista,ierr)
call mpi_send(buf_send, n_data, mpi_double_precision, dest, tag, mpi_comm_world, ,ierr)

このコードを実行すると、両方がデータの受信待ちになるものの、データをsendしてくれる相手がいないので、デッドロックになります。特にデータサイズが大きいときにおこりやすいです。

逆にしても同じです。

call mpi_send(buf_send, n_data, mpi_double_precision, dest, tag, mpi_comm_world, ierr)
call mpi_recv(buf_recv, n_data, mpi_double_precision, dest, tag, mpi_comm_world, ista,ierr)

mpi_sendは受信する相手がいないと正常に終了しません。なので順番を変えてもデッドロックになります。

厄介なことに、デッドロックは場合によっては起こったり起こらなかったりします。ログが出ることもありません。その上abortするわけでもありません1 。そのため「MPIプログラムが沈黙したらデッドロックを疑う」と知っていなければいけません。

解決策

1.MPI_sendrecvを使う

MPI_sendrecvを使えばデッドロックを防げます。

2. MPI_isendとMPI_irecvに置き換える

デッドロックはブロッキング通信であることが原因なので、ノンブロッキングであるMPI_isendとMPI_irecvに置き換えれば解消できます。

構造体やクラスを通信するには

さて、ここまでテストで扱ってきたmpiコマンドは、すべてmpiデータ型(mpi_integer,mpi_double_precisionなど)を引数に取ってきました。ではここで自分で定義した構造体やクラスなどを通信するにはどうすればよいでしょうか。

実は僕も1行で書けるコードは知りません。mpi_datatype_nullを駆使すれば行けるのかもしれないですが、僕が見たことがあるコードでは、構造体を1種類しか扱っていなかったので、その構造体に対してsendやbcastをそれぞれ定義していました。

これについては次回。

参考サイト


  1. この「abortしない」というのが非常に厄介です。「月曜日に結果を見るつもりで金曜日に計算を回してうまく行ってそうだったのに、月曜日になっても計算が終了しておらず結果も白紙のまま」ということが多々ありました。orz 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away