MPI二维矩阵散射问题:仅Rank 0结果正确,求错误排查
MPI矩阵散射分块问题排查
核心错误分析
你的代码仅Rank0能正确接收数据,其余进程失败,根源在于对MPI_Scatter的适用场景和子数组类型的理解偏差:
1. MPI_Scatter无法处理非连续内存块
MPI_Scatter默认要求发送缓冲区中的每个分发单元是连续的内存块,但你的矩阵分块(比如4×4矩阵的右上角、左下角、右下角子矩阵)在C语言行优先存储的1D数组中是非连续的。例如Rank1对应的右上角2×2块,元素在1D数组中的索引是2、3、6、7,并非连续的一块,MPI_Scatter无法自动拆分这类非连续数据。
2. 子数组类型的起始位置未对应分块
你创建的block_type固定使用starts={0,0},仅能描述左上角的子矩阵。但其他进程对应的分块起始坐标不同(Rank1是(0,2)、Rank2是(2,0)、Rank3是(2,2)),统一使用同一个子数组类型会导致发送端无法定位到正确的分块位置。
修正方案:改用MPI_Scatterv处理非连续分发
MPI_Scatterv支持为每个接收进程指定独立的发送缓冲区偏移,完美适配矩阵分块的非连续内存分布。以下是修正后的代码:
#include <stdio.h> #include <math.h> #include <stdlib.h> #include <mpi.h> int main(int argc, char *argv[]){ int rank, size; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); int n; if(rank == 0){ scanf("%d", &n); } MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); // 仅Rank0需要分配原矩阵内存 int **matrix = NULL; int *matrix_1D = NULL; if(rank == 0){ matrix = (int **)malloc(n * sizeof(int *)); matrix_1D = (int *)malloc(n * n * sizeof(int)); for(int i = 0; i < n; i++){ matrix[i] = (int *)malloc(n * sizeof(int)); } // 读取矩阵并转换为1D数组 for(int i = 0; i < n; i++) for(int j = 0; j < n; j++){ scanf(" %d", &matrix[i][j]); matrix_1D[i*n + j] = matrix[i][j]; } printf("原矩阵:\n"); for(int i=0; i<n; ++i){ for(int j=0; j<n; ++j){ printf("%d ", matrix[i][j]); } printf("\n"); } printf("\n"); } int new_n = n / 2; int local_size = new_n * new_n; int *local_matrix = (int *)malloc(local_size * sizeof(int)); // 确保使用4个进程(对应4个2×2分块) if(size != 4){ if(rank ==0) printf("必须使用4个进程运行\n"); MPI_Finalize(); return 1; } // 为MPI_Scatterv准备参数 int sendcounts[4]; int displs[4]; for(int i=0; i<4; i++){ sendcounts[i] = local_size; // 每个进程接收的元素数量 // 计算分块在1D数组中的起始偏移 int block_row = (i / 2) * new_n; int block_col = (i % 2) * new_n; displs[i] = block_row * n + block_col; } // 执行散射操作 MPI_Scatterv(matrix_1D, sendcounts, displs, MPI_INT, local_matrix, local_size, MPI_INT, 0, MPI_COMM_WORLD); // 打印接收结果 printf("Rank %d 接收的数据:\n", rank); for(int i = 0; i < new_n; i++){ for(int j = 0; j < new_n; j++){ printf("%d ", local_matrix[i*new_n + j]); } printf("\n"); } printf("\n"); // 释放内存 free(local_matrix); if(rank ==0){ free(matrix_1D); for(int i=0; i<n; i++) free(matrix[i]); free(matrix); } MPI_Finalize(); return 0; }
关键修改说明
- 改用MPI_Scatterv:通过
displs数组为每个进程指定分块在原1D矩阵中的起始偏移,解决非连续内存块的分发问题。 - 优化内存分配:仅Rank0分配原矩阵内存,避免其他进程的内存浪费和泄漏。
- 分块偏移计算:根据进程Rank计算对应的分块起始行和列,转换为1D数组的索引偏移,确保每个进程获取正确的子矩阵。
内容的提问来源于stack exchange,提问作者Ian Nemo
相关产品推荐
相关产品推荐

