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

  1#include <mpi.h>
  2#include <iostream>
  3#include <cstdlib>
  4#include <vector>
  5
  6/*
  7 * rank番号が自分の前後のプロセスと相互に送受信する。
  8 *
  9 * より大きいrankに向けてデータを送るのを「右向き」
 10 * より小さいrankに向けてデータを送るのを「左向き」
 11 * と呼ぶことにする。
 12 * 
 13 * 実行方法: mpiexec -n 4 mpi_sample10
 14 */
 15int main(int argc, char *argv[])
 16{
 17    MPI_Init(&argc, &argv);
 18
 19    int num_procs;
 20    int my_rank;
 21
 22    MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
 23    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
 24
 25    int send_data_count = 10;
 26    std::vector<double> send_left_data(send_data_count, 0);
 27    std::vector<double> send_right_data(send_data_count, 0);
 28
 29    // それぞれのrankで送信するデータを用意する
 30    for (int i = 0; i < send_data_count; i++)
 31    {
 32        // 左向きには負の値のデータを送る
 33        send_left_data[i] = - ((my_rank * 10) + i);
 34        send_right_data[i] = (my_rank * 10) + i;
 35    }
 36    // 送信データを表示する
 37    std::cout << "rank: " << my_rank << ", send_left_data: [";
 38    for (int i = 0; i < send_data_count; i++)
 39    {
 40        std::cout << " " << send_left_data[i];
 41    }
 42    std::cout << " ]" << std::endl;
 43    std::cout << "rank: " << my_rank << ", send_right_data: [";
 44    for (int i = 0; i < send_data_count; i++)
 45    {
 46        std::cout << " " << send_right_data[i];
 47    }
 48    std::cout << " ]" << std::endl;
 49
 50    int recv_data_count = 10;
 51    std::vector<double> recv_left_data(recv_data_count, 0); // 左向きの受信データ(右のノードから来たデータ)
 52    std::vector<double> recv_right_data(recv_data_count, 0); // 右向きの受信データ(左ノードから来たデータ)
 53
 54    int left_rank = (my_rank - 1 + num_procs) % num_procs;
 55    int right_rank = (my_rank + 1 + num_procs) % num_procs;
 56
 57    // ノンブロッキングの send/recv を使って、相互にデータを送受信する
 58    // 互いに同時に送信しても問題ない
 59    MPI_Request requests[4];
 60    MPI_Status statuses[4];
 61
 62    // 左向きに送信
 63    MPI_Isend(
 64        &send_left_data[0],
 65        send_data_count,
 66        MPI_DOUBLE,
 67        left_rank, // 送り先のrank
 68        1, // 左向きのデータにはタグとして 1 を割り当てることにする
 69        MPI_COMM_WORLD,
 70        &requests[0] // このノンブロッキング操作に対して発行されるIDをrequestsに受け取る
 71    );
 72    // 右向きに送信
 73    MPI_Isend(
 74        &send_right_data[0],
 75        send_data_count,
 76        MPI_DOUBLE,
 77        right_rank, // 送り先のrank
 78        2, // 右向きのデータにはタグとして 2 を割り当てることにする
 79        MPI_COMM_WORLD,
 80        &requests[1] // このノンブロッキング操作に対して発行されるIDをrequestsに受け取る
 81    );
 82
 83    // 左向きのデータを受信
 84    MPI_Irecv(
 85        &recv_left_data[0],
 86        recv_data_count,
 87        MPI_DOUBLE,
 88        right_rank, // 送り元のrank。左向きデータは右からやってくる
 89        1, // タグ。左向きデータなので 1
 90        MPI_COMM_WORLD,
 91        &requests[2] // このノンブロッキング操作に対して発行されるIDを記録する
 92    );
 93
 94    // 右向きのデータを受信
 95    MPI_Irecv(
 96        &recv_right_data[0],
 97        recv_data_count,
 98        MPI_DOUBLE,
 99        left_rank, // 送り元のrank。右向きデータは左からやってくる
100        2, // タグ。右向きデータなので 2
101        MPI_COMM_WORLD,
102        &requests[3] // このノンブロッキング操作に対して発行されるIDを記録する
103    );
104
105    // ノンブロッキング操作が完了するまで待つ。
106    // どの操作が完了するまで待つのかを、MPI_Request の配列で指定する。
107    // 操作ごとに失敗する可能性があるので、操作ごとの実行結果をMPI_Statusの
108    // 配列で受け取る
109
110    MPI_Waitall(
111        4, // 操作の数
112        &requests[0],
113        &statuses[0]
114    );
115
116    // 受信できたデータを表示する
117    std::cout << "rank: " << my_rank << ", recv_left_data: [";
118    for (int i = 0; i < recv_data_count; i++)
119    {
120        std::cout << " " << recv_left_data[i];
121    }
122    std::cout << " ]" << std::endl;
123
124    std::cout << "rank: " << my_rank << ", recv_right_data: [";
125    for (int i = 0; i < recv_data_count; i++)
126    {
127        std::cout << " " << recv_right_data[i];
128    }
129    std::cout << " ]" << std::endl;
130
131    MPI_Finalize();
132    return EXIT_SUCCESS;
133}

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 ]