P2P non-blocking

1. 非阻塞(non-blocking)通信

(1)非阻塞的sender

(a)几乎可以立刻返回去干别的事情。不管数据是否从application buffer到了system buffer, 或者数据到了receiver端的application buffer或者system buffer.

(b)发送操作由MPI lib选择合适的时间去完成。

(c)sender最好不要去立刻修改刚发送数据的内存单元(send buffer)(不安全啊!),但可以操作其他的内存单元。

(d)若要操作刚发送数据的内存单元(send buffer),须通过*wait*函数确认发送完成。

(e)非阻塞的发送可以实现计算与通信重叠(overlap computation with communication and exploit possible performance gains)。

(2) 非阻塞receiver(原理同非阻塞的sender)

2. 函数

MPI_Isend (&buf,count,datatype,dest,tag,comm,&request)

Identifies an area in memory to serve as a send buffer. Processing continues immediately without waiting for the message to be copied out from the application buffer. A communication request handle is returned for handling the pending message status. The program should not modify the application buffer until subsequent calls to MPI_Wait or MPI_Test indicate that the non-blocking send has completed.

MPI_Irecv (&buf,count,datatype,source,tag,comm,&request)

Identifies an area in memory to serve as a receive buffer. Processing continues immediately without actually waiting for the message to be received and copied into the the application buffer. A communication request handle is returned for handling the pending message status. The program must use calls to MPI_Wait or MPI_Test to determine when the non-blocking receive operation completes and the requested message is available in the application buffer.

3. 举例

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

int main(int argc, char *argv[]){
        int totalNumTasks, rankID;

        MPI_Init(&argc, &argv);
        MPI_Comm_size(MPI_COMM_WORLD, &totalNumTasks);
        MPI_Comm_rank(MPI_COMM_WORLD, &rankID);
        //get the host where this process is running
        int  nameLength;
        char processor_name[MPI_MAX_PROCESSOR_NAME];
        MPI_Get_processor_name(processor_name,&nameLength);

        int prevRankID = rankID - 1;
        int nextRankID = rankID + 1;
        if(rankID == 0)  prevRankID = totalNumTasks - 1;
        if(rankID == (totalNumTasks - 1)) nextRankID = 0;

        int count = 1;
        MPI_Request request[4];

        char recvBuf1;
        char sendBuf1 = 'R';
        int tag1 = 1;
        MPI_Irecv(&recvBuf1, count, MPI_CHAR, prevRankID, tag1, MPI_COMM_WORLD, &request[0]);
        MPI_Isend(&sendBuf1, count, MPI_CHAR, nextRankID, tag1, MPI_COMM_WORLD, &request[1]);

        char recvBuf2;
        char sendBuf2 = 'L';
        int tag2 = 2;
        MPI_Irecv(&recvBuf2, count, MPI_CHAR, nextRankID, tag2, MPI_COMM_WORLD, &request[2]);
        MPI_Isend(&sendBuf2, count, MPI_CHAR, prevRankID, tag2, MPI_COMM_WORLD, &request[3]);
        //after, non-blocking send and receive, process can do something except modifying the application buffer
        //Here, application buffer is recvBuf1, sendBuf1, recvBuf2, sendBuf2
        //which can overlap the communication and computing 
        //Indeed, you can use other memory areas
        printf("My rankID = %d on Processor = %s, I can do something here.....\n", rankID, processor_name);
        //Now to check after MPI_Waitall, after it, the application buffer is safe to reuse 
        MPI_Status status[4];
        MPI_Waitall(4, request, status);
        printf("My rankID = %d, recvBuf1 = %c && source = %d && tag = %d\n", 
                rankID, recvBuf1, status[0].MPI_SOURCE, status[0].MPI_TAG);

        printf("My rankID = %d, recvBuf2 = %c && source = %d && tag = %d\n", 
                rankID, recvBuf2, status[2].MPI_SOURCE, status[2].MPI_TAG);
        printf("My rankID = %d, Now, my application buffer is safe to reuse.\n", rankID);
        MPI_Finalize();
        return 0;
}

4. 编译执行

[amao@amao991 mpi-study]$ mpicc p2pNonBlockingOnWhichProcessor.c 
[amao@amao991 mpi-study]$ mpiexec -n 3 -f machinefile ./a.out 
My rankID = 0 on Processor = amao991, I can do something here.....
My rankID = 2 on Processor = amao992, I can do something here.....
My rankID = 1 on Processor = amao991, I can do something here.....
My rankID = 0, recvBuf1 = R && source = 2 && tag = 1
My rankID = 0, recvBuf2 = L && source = 1 && tag = 2
My rankID = 0, Now, my application buffer is safe to reuse.
My rankID = 1, recvBuf1 = R && source = 0 && tag = 1
My rankID = 1, recvBuf2 = L && source = 2 && tag = 2
My rankID = 1, Now, my application buffer is safe to reuse.
My rankID = 2, recvBuf1 = R && source = 1 && tag = 1
My rankID = 2, recvBuf2 = L && source = 0 && tag = 2
My rankID = 2, Now, my application buffer is safe to reuse.

5. 总结

(1)本例中3个进程构成一个双向环,每个进程接收到消息后,就转手发送

(2)由于是采用了non-blocking发送/接受,因此,函数调用完成后,进程可以继续执行其他语句(这里用printf一条语句来示例),只要不操作接受/发送buffer就好

(3)到最后,若要查看接收buffer,须调用MPI_Waitall以确保接受完成了。


你可能感兴趣的:(P2P non-blocking)