C++でOpenMPI入門8 一対一でノンブロッキング通信

話がちょっと戻るが, 入門のその6で試したブロッキング通信をノンブロッキング通信を用いてやってみた.

http://d.hatena.ne.jp/bettamodoki/20120627/1340797509

ブロッキング通信では, 送受信が完了するまでその関数を出なかったが, ノンブロッキング通信では送受信が完了する前に次の作業に進むことが出来る.

もちろん, そのままだと完全に投げっぱなしなので送受信する意味が無いので, 必要になったところでWaitをかけて送受信の完了を待つ.

#include <stdio.h>
#include <mpi.h>

int main(int argc, char **argv)
{
    MPI::Init(argc, argv);

    int rank = MPI::COMM_WORLD.Get_rank();

    int send_value = rank;
    int recv_value = -1;

    int const TAG = 0;

    if (rank == 0)
    {
        MPI::Request request = MPI::COMM_WORLD.Isend(
            &send_value, 1, MPI::INT, 1, TAG);
        ; // do something here                                                                                                                                                                                                                                                       
        request.Wait();
    }
    else if (rank == 1)
    {
        MPI::Request request = MPI::COMM_WORLD.Irecv(
            &recv_value, 1, MPI::INT, 0, TAG);
        ; // do something here                                                                                                                                                                                                                                                       
        MPI::Status status;
        request.Wait(status);
    }

    std::cout << "rank = " << rank
              << " : recv_value = " << recv_value << std::endl;

    MPI::Finalize();
}

変わったのはIsend, Irecvの部分とその返り値であるMPI::Request. 使い方は以前のSend, Recvと同じなのでまあ良かろう.

MPI::Requestはこの送受信の完了を待つときに必要となるもので, request.Wait()のようにして使う. ノンブロッキング通信なので送受信を開始してからWaitで完了するまでの間に作業をこなすことができるらしい.

作業ということは, もちろん他の送受信も行えるということなので, Sendrecvの代わりにノンブロッキング通信を用いて以下のようにも書ける.

    if (rank == 0)
    {
        MPI::Request request[2];
        request[0] = MPI::COMM_WORLD.Isend(&send_value, 1, MPI::INT, 1, TAG);
        request[1] = MPI::COMM_WORLD.Irecv(&recv_value, 1, MPI::INT, 1, TAG);
        MPI::Request::Waitall(2, request);
    }
    else if (rank == 1)
    {
        MPI::Request request[2];
        request[0] = MPI::COMM_WORLD.Isend(&send_value, 1, MPI::INT, 0, TAG);
        request[1] = MPI::COMM_WORLD.Irecv(&recv_value, 1, MPI::INT, 0, TAG);
        MPI::Request::Waitall(2, request);
    }

ブロッキング通信のときと違って, Isend, Irecvの順番が0と1で同じでもノンブロッキング通信ではデッドロックが発生しない. Waitallはreqeustで与えられたMPI::Request全てが完了するまで待つ. 1番目の引数はrequestが含むMPI::Requestの数(配列長). 3番目の引数としてMPI::Statusの配列を指定することもできます.

http://mikilab.doshisha.ac.jp/dia/smpp/cluster2000/PDF/chapter02.pdf