(done) 自学 MPI (7) MPI 消息传递例程 (阻塞 和 非阻塞)

三个参考链接:
url1: https://hpc-tutorials.llnl.gov/mpi/routine_args/
url2: https://hpc-tutorials.llnl.gov/mpi/blocking/
url3: https://hpc-tutorials.llnl.gov/mpi/non_blocking/


对于各个库函数/例程的介绍这里就不赘述,建议看三个参考 url 自行理解。

这里只研究样例代码。

样例1:ping pong (阻塞)

样例代码1 (精华都在注释):
进程0 给 进程1 发送 ping 并等待返回的 ping

#include "mpi.h"  // MPI头文件
#include <stdio.h> // 标准输入输出头文件

int main(int argc, char *argv[]) {
	// MPI相关变量声明
	int numtasks,  // MPI任务总数(进程数)
	    rank,      // 当前任务的排名(进程ID)
	    dest,      // 目标进程排名(消息发送目的地)
	    source,    // 源进程排名(消息来源)
	    rc,        // 返回值,用于检查MPI函数调用状态
	    count,     // 实际接收到的消息元素数量
	    tag=1;     // 消息标签,用于区分不同类型的消息
	
	char inmsg,    // 接收消息的缓冲区
	     outmsg='x'; // 发送消息,初始化为字符'x'
	
	MPI_Status Stat;   // MPI状态对象,用于接收操作中存储消息状态信息

	// 初始化MPI环境
	MPI_Init(&argc,&argv);
	// 获取通信域中的进程总数
	MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
	// 获取当前进程在通信域中的排名(ID)
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	// 任务0:先发送消息到任务1,然后等待接收任务1的回复
	if (rank == 0) {
    	dest = 1;    // 设置目标进程为任务1
    	source = 1;  // 设置期望接收消息的源进程为任务1
    	// 阻塞发送:发送outmsg中的1个MPI_CHAR类型数据到目标进程
    	MPI_Send(&outmsg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
    	// 阻塞接收:从源进程接收1个MPI_CHAR类型数据到inmsg
    	MPI_Recv(&inmsg, 1, MPI_CHAR, source, tag, MPI_COMM_WORLD, &Stat);
    }
	// 任务1:先等待接收任务0的消息,然后发送回复消息
	else if (rank == 1) {
    	dest = 0;    // 设置目标进程为任务0
    	source = 0;  // 设置期望接收消息的源进程为任务0
    	// 阻塞接收:先接收任务0发来的消息
    	MPI_Recv(&inmsg, 1, MPI_CHAR, source, tag, MPI_COMM_WORLD, &Stat);
    	// 阻塞发送:然后发送回复消息给任务0
    	MPI_Send(&outmsg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
    }

	// 查询接收状态并打印消息详情
	// 获取实际接收到的数据元素数量
	MPI_Get_count(&Stat, MPI_CHAR, &count);
	// 打印接收信息:任务ID、接收字符数、来源任务、消息标签
	printf("Task %d: Received %d char(s) from task %d with tag %d \n",
        rank, count, Stat.MPI_SOURCE, Stat.MPI_TAG);

	// 终止MPI环境
	MPI_Finalize();
	
	return 0; // 程序正常退出
}

编译运行方式:

mpicc test.c -o test
mpirun -np 2 ./test

预期输出:

Task 0: Received 1 char(s) from task 1 with tag 1 
Task 1: Received 1 char(s) from task 0 with tag 1 

样例2:环形拓扑 (非阻塞)

Nearest neighbor exchange in a ring topology
在这里插入图片描述

#include "mpi.h"  // MPI头文件
#include <stdio.h> // 标准输入输出头文件

int main(int argc, char *argv[]) {
    // MPI相关变量声明
    int numtasks,    // MPI任务总数(进程数)
        rank,        // 当前任务的排名(进程ID)
        next,        // 右邻居进程排名(环形拓扑)
        prev,        // 左邻居进程排名(环形拓扑)
        buf[2],      // 接收缓冲区,用于存储来自两个邻居的消息
        tag1=1,      // 消息标签1,用于区分消息类型
        tag2=2;      // 消息标签2,用于区分消息类型
    
    MPI_Request reqs[4];   // 非阻塞操作请求句柄数组
    MPI_Status stats[4];   // 状态数组,用于Waitall routine

    // 初始化MPI环境
    MPI_Init(&argc,&argv);
    // 获取通信域中的进程总数
    MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
    // 获取当前进程在通信域中的排名(ID)
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    // 确定左右邻居(创建环形进程拓扑)
    prev = rank-1;  // 左邻居通常是rank-1
    next = rank+1;  // 右邻居通常是rank+1
    
    // 处理边界情况,形成环形拓扑
    if (rank == 0)  prev = numtasks - 1;           // 第一个进程的左邻居是最后一个进程
    if (rank == (numtasks - 1))  next = 0;         // 最后一个进程的右邻居是第一个进程

    // 发布非阻塞接收操作(立即返回,不等待消息到达)
    // 从左邻居接收标签为tag1的消息,存储到buf[0]
    MPI_Irecv(&buf[0], 1, MPI_INT, prev, tag1, MPI_COMM_WORLD, &reqs[0]);
    // 从右邻居接收标签为tag2的消息,存储到buf[1]  
    MPI_Irecv(&buf[1], 1, MPI_INT, next, tag2, MPI_COMM_WORLD, &reqs[1]);

    // 发布非阻塞发送操作(立即返回,不等待发送完成)
    // 发送当前进程rank值给左邻居,使用标签tag2
    MPI_Isend(&rank, 1, MPI_INT, prev, tag2, MPI_COMM_WORLD, &reqs[2]);
    // 发送当前进程rank值给右邻居,使用标签tag1
    MPI_Isend(&rank, 1, MPI_INT, next, tag1, MPI_COMM_WORLD, &reqs[3]);

    // 在此处可以执行一些计算工作
    // 非阻塞操作的优点:通信与计算可以重叠进行
    // 当MPI库在后台处理消息传递时,程序可以继续执行其他计算任务
    // do some work while sends/receives progress in background

    // 等待所有非阻塞操作完成
    // 阻塞在此处,直到4个请求(2个接收+2个发送)全部完成
    MPI_Waitall(4, reqs, stats);

    // 继续执行 - 进行更多工作
    // 此时可以安全地使用buf中的数据,因为所有通信已完成
    // continue - do more work

    // 终止MPI环境
    MPI_Finalize();
    
    return 0; // 程序正常退出
}

编译运行命令:

mpicc test.c -o test
mpirun -np 4 ./test

预期没有输出


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值