2.3.10. 全隣接rank同士の相互通信

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <mpi.h>
#include <iostream>
#include <cstdlib>
#include <vector>

/*
 * rank番号が自分の前後のプロセスと相互に送受信する。
 *
 * より大きいrankに向けてデータを送るのを「右向き」
 * より小さいrankに向けてデータを送るのを「左向き」
 * と呼ぶことにする。
 * 
 * 実行方法: mpiexec -n 4 mpi_sample10
 */
int main(int argc, char *argv[])
{
    MPI_Init(&argc, &argv);

    int num_procs;
    int my_rank;

    MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    int send_data_count = 10;
    std::vector<double> send_left_data(send_data_count, 0);
    std::vector<double> send_right_data(send_data_count, 0);

    // それぞれのrankで送信するデータを用意する
    for (int i = 0; i < send_data_count; i++)
    {
        // 左向きには負の値のデータを送る
        send_left_data[i] = - ((my_rank * 10) + i);
        send_right_data[i] = (my_rank * 10) + i;
    }
    // 送信データを表示する
    std::cout << "rank: " << my_rank << ", send_left_data: [";
    for (int i = 0; i < send_data_count; i++)
    {
        std::cout << " " << send_left_data[i];
    }
    std::cout << " ]" << std::endl;
    std::cout << "rank: " << my_rank << ", send_right_data: [";
    for (int i = 0; i < send_data_count; i++)
    {
        std::cout << " " << send_right_data[i];
    }
    std::cout << " ]" << std::endl;

    int recv_data_count = 10;
    std::vector<double> recv_left_data(recv_data_count, 0); // 左向きの受信データ(右のノードから来たデータ)
    std::vector<double> recv_right_data(recv_data_count, 0); // 右向きの受信データ(左ノードから来たデータ)

    int left_rank = (my_rank - 1 + num_procs) % num_procs;
    int right_rank = (my_rank + 1 + num_procs) % num_procs;

    // ノンブロッキングの send/recv を使って、相互にデータを送受信する
    // 互いに同時に送信しても問題ない
    MPI_Request requests[4];
    MPI_Status statuses[4];

    // 左向きに送信
    MPI_Isend(
        &send_left_data[0],
        send_data_count,
        MPI_DOUBLE,
        left_rank, // 送り先のrank
        1, // 左向きのデータにはタグとして 1 を割り当てることにする
        MPI_COMM_WORLD,
        &requests[0] // このノンブロッキング操作に対して発行されるIDをrequestsに受け取る
    );
    // 右向きに送信
    MPI_Isend(
        &send_right_data[0],
        send_data_count,
        MPI_DOUBLE,
        right_rank, // 送り先のrank
        2, // 右向きのデータにはタグとして 2 を割り当てることにする
        MPI_COMM_WORLD,
        &requests[1] // このノンブロッキング操作に対して発行されるIDをrequestsに受け取る
    );

    // 左向きのデータを受信
    MPI_Irecv(
        &recv_left_data[0],
        recv_data_count,
        MPI_DOUBLE,
        right_rank, // 送り元のrank。左向きデータは右からやってくる
        1, // タグ。左向きデータなので 1
        MPI_COMM_WORLD,
        &requests[2] // このノンブロッキング操作に対して発行されるIDを記録する
    );

    // 右向きのデータを受信
    MPI_Irecv(
        &recv_right_data[0],
        recv_data_count,
        MPI_DOUBLE,
        left_rank, // 送り元のrank。右向きデータは左からやってくる
        2, // タグ。右向きデータなので 2
        MPI_COMM_WORLD,
        &requests[3] // このノンブロッキング操作に対して発行されるIDを記録する
    );

    // ノンブロッキング操作が完了するまで待つ。
    // どの操作が完了するまで待つのかを、MPI_Request の配列で指定する。
    // 操作ごとに失敗する可能性があるので、操作ごとの実行結果をMPI_Statusの
    // 配列で受け取る

    MPI_Waitall(
        4, // 操作の数
        &requests[0],
        &statuses[0]
    );

    // 受信できたデータを表示する
    std::cout << "rank: " << my_rank << ", recv_left_data: [";
    for (int i = 0; i < recv_data_count; i++)
    {
        std::cout << " " << recv_left_data[i];
    }
    std::cout << " ]" << std::endl;

    std::cout << "rank: " << my_rank << ", recv_right_data: [";
    for (int i = 0; i < recv_data_count; i++)
    {
        std::cout << " " << recv_right_data[i];
    }
    std::cout << " ]" << std::endl;

    MPI_Finalize();
    return EXIT_SUCCESS;
}

Download the source code

MPI_Isend と MPI_Irecv を使ったさらに踏み込んだ例として、 任意の数のプロセス数で起動された際に、前後のrankと相互に データを送り合う例を示します。

openmpiでの実行例を示します。:

$ mpic++ -o mpi_sample10 mpi_sample10.cpp
$ mpiexec -n 4 mpi_sample10
rank: 2, send_left_data: [ -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 ]
rank: 2, send_right_data: [ 20 21 22 23 24 25 26 27 28 29 ]
rank: 0, send_left_data: [ 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 ]
rank: 0, send_right_data: [ 0 1 2 3 4 5 6 7 8 9 ]
rank: 1, send_left_data: [ -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 ]
rank: 3, send_left_data: [ -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 ]
rank: 1, send_right_data: [ 10 11 12 13 14 15 16 17 18 19 ]
rank: 3, send_right_data: [ 30 31 32 33 34 35 36 37 38 39 ]
rank: 0, recv_left_data: [ -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 ]
rank: 0, recv_right_data: [ 30 31 32 33 34 35 36 37 38 39 ]
rank: 1, recv_left_data: [ -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 ]
rank: 3, recv_left_data: [ 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 ]
rank: 3, recv_right_data: [ 20 21 22 23 24 25 26 27 28 29 ]
rank: 2, recv_left_data: [ -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 ]
rank: 2, recv_right_data: [ 10 11 12 13 14 15 16 17 18 19 ]
rank: 1, recv_right_data: [ 0 1 2 3 4 5 6 7 8 9 ]