使用C语言实现MPI矩阵向量乘法时出现段错误,求解决及原理讲解
矩阵向量乘法MPI实现段错误的原因与修复
错误原因分析
MPI_Scatter不支持分散二维指针数组
你使用的double **matrix是指针数组,内存并非连续块,MPI_Scatter无法正确识别和分散这类非连续内存结构,直接调用会导致指针越界触发段错误。同时所有进程都分配了n行的矩阵,非0进程根本不需要这么大的内存,属于内存浪费且加剧了指针混乱。内存释放逻辑错误
二维指针数组不能直接用free(matrix)释放,必须先逐行释放每个matrix[i],再释放顶层指针,否则会导致内存泄漏或非法释放错误。结果向量c的维度错误
rank=0中,结果向量c对应矩阵n行的乘积结果,是n维向量,但你却分配了m个元素,后续MPI_Gather时会越界访问内存,触发段错误。printf格式符错误
输出语句printf(" .2%f", c[i])的格式符顺序错误,正确写法是%.2f,错误格式会导致输出异常。非0进程的不必要内存分配
非0进程不需要分配全局大小的矩阵,也不需要提前分配b的内存(可在MPI_Bcast前按需分配),多余的内存分配可能引发野指针问题。
修复后的完整代码
#include<stdio.h> #include<stdlib.h> #include "mpi.h" #define n 4 #define m 4 void matrix_vector_product(double **matrix, double *vector, double *result, int rows, int cols) { for(int i=0; i<rows; i++) result[i] = 0; for(int i=0; i<rows; i++) for(int j=0; j<cols; j++) result[i] += matrix[i][j] * vector[j]; } int main(int argc, char *argv[]) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); double **matrix = NULL; double *b = NULL; double *c = NULL; double *local_c = NULL; int local_n = n / size; // 仅rank0分配全局矩阵和结果向量c if (rank == 0) { matrix = (double **)calloc(n, sizeof(double *)); for (int i=0; i<n; i++) { matrix[i] = (double *)calloc(m, sizeof(double)); } // 初始化全局矩阵 for(int i=0; i<n; i++) for(int j =0; j<m; j++) matrix[i][j] = i + j; // 分配并初始化向量b b = (double *)calloc(m, sizeof(double)); for(int j =0; j<m; j++) b[j] = j + 1; // 结果向量c是n维,匹配矩阵行数 c = (double *)calloc(n, sizeof(double)); } else { // 非0进程仅分配局部所需的local_n行矩阵 matrix = (double **)calloc(local_n, sizeof(double *)); for (int i=0; i<local_n; i++) { matrix[i] = (double *)calloc(m, sizeof(double)); } // 非0进程分配向量b的内存 b = (double *)calloc(m, sizeof(double)); } // 分配局部结果向量 local_c = (double *)calloc(local_n, sizeof(double)); // 传递全局矩阵的连续内存首地址,适配MPI_Scatter的连续内存要求 MPI_Scatter(matrix[0], local_n * m, MPI_DOUBLE, matrix[0], local_n * m, MPI_DOUBLE, 0, MPI_COMM_WORLD); // 广播向量b到所有进程 MPI_Bcast(b, m, MPI_DOUBLE, 0, MPI_COMM_WORLD); // 计算局部矩阵向量乘积 matrix_vector_product(matrix, b, local_c, local_n, m); // 收集局部结果到全局向量c MPI_Gather(local_c, local_n, MPI_DOUBLE, c, local_n, MPI_DOUBLE, 0, MPI_COMM_WORLD); // 输出结果 if(rank == 0){ printf(" Result = "); for(int i=0; i<n; i++) printf(" %.2f", c[i]); printf("\n"); } // 规范释放内存:先逐行释放二维数组,再释放顶层指针 if (matrix != NULL) { int rows = (rank == 0) ? n : local_n; for(int i=0; i<rows; i++) { free(matrix[i]); } free(matrix); } free(b); free(c); free(local_c); MPI_Finalize(); return 0; }
关键MPI概念巩固
- 连续内存要求:MPI集体通信函数(Scatter、Gather、Bcast等)要求缓冲区是连续内存块。如果使用二维数组,推荐用连续分配的二维数组(比如
double (*matrix)[m] = malloc(local_n * sizeof(*matrix))),或者将二维数组转换为一维数组传递,避免指针数组的非连续内存问题。 - 进程内存独立性:每个MPI进程拥有独立的地址空间,非0进程只需要分配自己计算所需的局部内存,不需要分配全局数据结构,减少内存浪费和错误概率。
- 内存管理规范:多维数组的分配和释放必须匹配,逐行分配的二维数组要逐行释放,避免内存泄漏或非法内存访问。
内容的提问来源于stack exchange,提问作者TCOD18 Siddharth Hemnani
相关产品推荐
相关产品推荐

