MPI_Recv 从接收缓冲区指定位置存储数据的实现问题排查
你的程序从最初版本就存在自定义MPI派生类型的计数重复错误,修改recv_buf后出现的无输出阻塞,是这个遗留问题叠加MPI底层内存越界保护的直接结果。
具体错误拆解
对自定义MPI类型的语义理解偏差
MPI_Type_contiguous(r, MPI_DOUBLE, &type)的作用是创建新的派生数据类型,单个该类型实例本身就等价于连续排布的r(也就是4)个MPI_DOUBLE元素。即1个type对象 = 4个double值。
但你在后续MPI_Send、MPI_Recv的调用中,count参数依然传入r=4,这会导致实际通信的总元素规模变为:传入的count值 * 单个自定义类型的元素数 = 4 * 4 = 16个double。最初版本能输出的本质原因
最初版本的recv_buf仅分配了4个double的栈空间,你让MPI往这块只有4个元素的缓冲区里写入16个double,属于栈内存踩踏的未定义行为。只是恰好写入的前4个值就是你要发送的mat数组内容,才凑巧输出了1 2 3 4,程序本身已经处于内存损坏的异常状态,只是没有触发崩溃而已。修改后程序无输出的原因
你把recv_buf调整为10个double长度后,接收缓冲区的起始位置设在&recv_buf[6],从这个位置往后只有4个double的可用存储空间。但你的接收逻辑依然要求接收总大小为16个double的数据流,MPI底层会检测到接收缓冲区的可用空间完全无法容纳预期数据,直接不完成通信匹配,导致MPI_Recv永久阻塞,程序根本走不到后续的打印环节,自然没有任何输出。
正确修改方案
既然已经用MPI_Type_contiguous定义好了包含4个double的连续类型,收发时count参数传1即可,代表传递1个完整的自定义类型实例:
- 0号进程发送代码调整为:
MPI_Send(mat, 1, type, 1, 100, MPI_COMM_WORLD); - 1号进程接收代码调整为:
MPI_Recv(&recv_buf[6], 1, type, 0, 100, MPI_COMM_WORLD, &status[0]);
修改后通信总数据量刚好是4个double,从recv_buf的下标6位置开始存放,正好占用6-9的下标空间,既不会越界,也能正确完成通信匹配。
冗余代码说明
你代码中定义的snd_buf、buf、position、长度为r的status数组全程没有实际作用,属于无意义的冗余声明,不影响运行但会增加理解成本,可以按需删除。MPI的接收状态变量只需要单个MPI_Status实例即可,不需要开辟数组。
内容的提问来源于stack exchange,提问作者Turing101

