如何使用MPI_Scatter拆分数据并分发至除根进程外的所有进程
MPI根进程仅分发不保留数据的分片实现方法
场景说明
- 测试集群共4个MPI进程,rank编号0~3
- 初始状态:rank 0持有完整数据集
[1,2,3,4,5,6],rank1、rank2、rank3初始持有空数组[] - 预期效果:分发完成后rank 0持有空数组
[],rank1获取分片[1,2]、rank2获取[3,4]、rank3获取[5,6],根进程只承担分发职责,不保留任何分片 - 常见踩坑:很多人第一反应是搭配
MPI_IN_PLACE调用MPI_Scatter实现,但根本跑不通——MPI_IN_PLACE在Scatter接口中的设计作用是让根进程跳过接收步骤、保留自身内存中对应位置的分片,和「根进程不保留任何分片」的需求完全相反。
可直接落地的两种实现
方案1:MPI_Scatterv 自定义分片长度(性能最优,推荐)
标准MPI_Scatter要求所有进程的分片长度一致,默认会给根进程也分配一份分片,不适合当前场景。换成支持可变分片长度的MPI_Scatterv,给根进程设置收发长度为0即可,一次集合通信就能完成分发,比点对点通信效率高。
核心代码逻辑(C风格伪代码):
int sendcounts[4] = {0, 2, 2, 2}; // 各进程接收的元素个数,rank0设为0 int displs[4] = {0, 0, 2, 4}; // 各分片在根进程原数组中的偏移位置,rank0的偏移无实际意义可随意填 int *recv_buf = NULL; // 非根进程提前分配接收缓存 if (rank != 0) { recv_buf = (int*)malloc(2 * sizeof(int)); } MPI_Scatterv( rank == 0 ? full_array : NULL, // 只有根进程传入完整原数组 sendcounts, displs, MPI_INT, recv_buf, rank == 0 ? 0 : 2, // 根进程接收长度设为0,不接收任何数据 MPI_INT, 0, // 指定根进程为rank0 MPI_COMM_WORLD ); // 根进程分发完成后直接释放原数组内存,即达到持有空数组的状态 if (rank == 0) { free(full_array); full_array = NULL; }
注意:MPI标准明确允许Scatterv接口中给任意进程设置sendcount为0,不会触发通信错误,该写法完全符合MPI规范,可在所有主流MPI实现(OpenMPI、MPICH、Intel MPI)上正常运行。
方案2:点对点通信发送(逻辑简单,无参数门槛)
如果进程规模不大,或者对Scatterv的偏移、计数参数不熟悉,直接用点对点MPI_Send/MPI_Recv实现即可,逻辑直观不容易写错。
核心代码逻辑:
if (rank == 0) { // 按分片给其余三个进程发数据 MPI_Send(full_array, 2, MPI_INT, 1, 0, MPI_COMM_WORLD); MPI_Send(full_array + 2, 2, MPI_INT, 2, 0, MPI_COMM_WORLD); MPI_Send(full_array + 4, 2, MPI_INT, 3, 0, MPI_COMM_WORLD); // 发完释放原数组 free(full_array); full_array = NULL; } else { recv_buf = (int*)malloc(2 * sizeof(int)); MPI_Recv(recv_buf, 2, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); }
这个写法没有集合通信的参数配置成本,代码可读性高,进程数少于100的时候性能差异可以忽略。
内容的提问来源于stack exchange,提问作者Liang
相关产品推荐
相关产品推荐

