MPICH笔记(四):非阻塞通信

MPICH笔记(四):非阻塞通信

官方文档链接:

int MPI_Isend(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Wait(MPI_Request *request, MPI_Status *status)

int MPI_Waitall(int count, MPI_Request array_of_requests[],  MPI_Status array_of_statuses[])

int MPI_Request_free(MPI_Request *request)

概述:

MPI的非阻塞通信的函数的命名规则一般是在对应的阻塞式函数的第二个单词处开头添加大写I,表示这个函数时非阻塞的,并且函数的参数会有一定的调整(如MPI_Irecv()比MPI_Recv()少了输出MPI_Status类型数据的参数),并都添加上了输出MPI_Request类型数据的参数,用来在之后的程序中进行判断通信是否完成。

代码:

#include <iostream>
#include "mpi.h"

using namespace std;

typedef long long LL;

int main(void) {
    MPI_Init(nullptr, nullptr);
    int rank, size;
    int dat;
    MPI_Request *r;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if (!rank) {
        r = new MPI_Request[size - 1];
        dat = 233;
        /*
        * 进行非阻塞式点对点发送
        * 与具有rank i的进程的通信状态存储在r[i - 1]中
        * 即地址r + i - 1上     
        */
        for (int i = 1; i < size; i++) {
            MPI_Isend(&dat, 1, MPI_INT, i, 0, MPI_COMM_WORLD, r + i - 1);
        }
        /*
        * 进行一个比较耗时的计算,这里使用循环来暴力计算10到100的阶乘之和
        * 对1000000007取模的值
        */
        LL mod = 1000000007ll;
        LL sum = 0, ret;
        for (int i = 10; i <= 100; i++) {
            ret = 1;
            for (int j = 1; j <= i; j++) {
                ret = (ret * i) % mod;
            }
            sum = (sum + ret) % mod;
            printf("Rank 0 > do operation %d!\n", i);
        }
        MPI_Waitall(size - 1, r, MPI_STATUSES_IGNORE);
        delete [] r;
    } else {
        r = new MPI_Request();
        MPI_Irecv(&dat, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, r);
        for (int i = 0; i < 1000; i++)
            for (int j = 0; j < 1000000; j++);
        MPI_Wait(r, MPI_STATUS_IGNORE);
        printf("Rank %d > Done!\n", rank);
        delete r;
    }
    MPI_Finalize();
    return 0;
}

执行指令:

mpiexec -n 8 ./文件名

输出:

Rank 0 > do operation 10!
Rank 0 > do operation 11!
Rank 0 > do operation 12!
Rank 0 > do operation 13!
Rank 0 > do operation 14!
Rank 0 > do operation 15!
Rank 0 > do operation 16!
Rank 0 > do operation 17!
Rank 0 > do operation 18!
Rank 0 > do operation 19!
Rank 0 > do operation 20!
Rank 0 > do operation 21!
Rank 0 > do operation 22!
Rank 0 > do operation 23!
Rank 0 > do operation 24!
Rank 0 > do operation 25!
Rank 0 > do operation 26!
Rank 0 > do operation 27!
Rank 0 > do operation 28!
Rank 0 > do operation 29!
Rank 0 > do operation 30!
Rank 0 > do operation 31!
Rank 0 > do operation 32!
Rank 0 > do operation 33!
Rank 0 > do operation 34!
Rank 0 > do operation 35!
Rank 0 > do operation 36!
Rank 0 > do operation 37!
Rank 0 > do operation 38!
Rank 0 > do operation 39!
Rank 0 > do operation 40!
Rank 0 > do operation 41!
Rank 0 > do operation 42!
Rank 0 > do operation 43!
Rank 0 > do operation 44!
Rank 0 > do operation 45!
Rank 0 > do operation 46!
Rank 0 > do operation 47!
Rank 0 > do operation 48!
Rank 0 > do operation 49!
Rank 0 > do operation 50!
Rank 0 > do operation 51!
Rank 0 > do operation 52!
Rank 0 > do operation 53!
Rank 0 > do operation 54!
Rank 0 > do operation 55!
Rank 0 > do operation 56!
Rank 0 > do operation 57!
Rank 0 > do operation 58!
Rank 0 > do operation 59!
Rank 0 > do operation 60!
Rank 0 > do operation 61!
Rank 0 > do operation 62!
Rank 0 > do operation 63!
Rank 0 > do operation 64!
Rank 0 > do operation 65!
Rank 0 > do operation 66!
Rank 0 > do operation 67!
Rank 0 > do operation 68!
Rank 0 > do operation 69!
Rank 0 > do operation 70!
Rank 0 > do operation 71!
Rank 0 > do operation 72!
Rank 0 > do operation 73!
Rank 0 > do operation 74!
Rank 0 > do operation 75!
Rank 0 > do operation 76!
Rank 0 > do operation 77!
Rank 0 > do operation 78!
Rank 0 > do operation 79!
Rank 0 > do operation 80!
Rank 0 > do operation 81!
Rank 0 > do operation 82!
Rank 0 > do operation 83!
Rank 0 > do operation 84!
Rank 0 > do operation 85!
Rank 0 > do operation 86!
Rank 0 > do operation 87!
Rank 0 > do operation 88!
Rank 0 > do operation 89!
Rank 0 > do operation 90!
Rank 0 > do operation 91!
Rank 0 > do operation 92!
Rank 0 > do operation 93!
Rank 0 > do operation 94!
Rank 0 > do operation 95!
Rank 0 > do operation 96!
Rank 0 > do operation 97!
Rank 0 > do operation 98!
Rank 0 > do operation 99!
Rank 0 > do operation 100!
Rank 1 > Done!
Rank 4 > Done!
Rank 6 > Done!
Rank 3 > Done!
Rank 5 > Done!
Rank 2 > Done!
Rank 7 > Done!

非阻塞式通信通常会结合MPI_Wait()或MPI_Waitall()来使用,这样一方面可以避免阻塞式通信带来的延时,可以直接去执行通信环节之后的与前面通信结果无关的操作,又可以在需要使用到前面通信结果之前调用MPI_Wait()或MPI_Waitall()来阻塞,确保通信的完成。

代码概述:

MPI_Waitall()

这个函数的第三个参数是输出参数,是一个具有与第一个参数指定的元素个数的MPI_Status数组,因此,在不需要的时候,这里需要传递的是预定义常量MPI_STATUSES_IGNORE,而不是MPI_STATUS_IGNORE。

MPI_Request_free()

用于释放掉通常由如上类似的非阻塞函数传递的MPI_Request对象,注意,一旦释放后,将不能使用它传递给Wait函数来进行阻塞。