基于MPI的C++矩阵乘法代码问题:结果不一致与多进程崩溃
矩阵乘法并行程序的问题排查与修复
问题1:串行与并行计算结果不一致
原因分析
- 累加逻辑错误:并行函数中每次处理矩阵A的一列时,都会将
result[i][j]重置为0再重新计算,而矩阵乘法的正确逻辑是对每一轮乘积进行累加,覆盖式赋值导致最终结果仅保留最后一轮计算值。 - 变量名冲突:并行函数外层循环变量为
k,内层点积循环变量也用k,导致循环逻辑混乱,变量值被意外覆盖。 - 并行结果未合并:原代码中每个进程仅计算部分列,但未将各进程的计算结果汇总到0进程,导致0进程的并行结果不完整,对比时出现不一致。
修复步骤
- 删除并行函数中
result[i][j] = 0;代码(结果矩阵初始化时已设为0,后续只需累加)。 - 将内层点积循环变量名从
k改为m,避免与外层循环变量冲突。 - 新增并行结果合并逻辑:将各进程计算的部分列数据汇总到0进程,形成完整的乘积矩阵。
问题2:多进程运行崩溃
原因分析
- 空指针访问:非0进程的
matrixA和matrixB未分配内存,直接传递给计算函数或MPI_Bcast会触发内存访问错误。 - 无效函数调用:非0进程没有完整的
matrixA数据,却调用multiplySerial函数,导致空指针解引用崩溃。 - MPI通信逻辑错误:原代码使用
Allgather传递矩阵A的列数据,但只有0进程持有完整的matrixA,非0进程无有效数据可发送,引发通信错误。
修复步骤
- 统一分配
matrixB内存:所有进程都为matrixB分配内存后,再执行MPI_Bcast接收数据。 - 限制串行计算范围:仅0进程执行串行计算,非0进程跳过该步骤,避免空指针访问。
- 修正MPI通信逻辑:并行函数改用
MPI_Bcast从0进程广播矩阵A的列数据,而非Allgather。
完整修复后的代码
全局函数与头文件
#include <iostream> #include <cstdlib> #include <ctime> #include <mpi.h> using namespace std; // 生成随机矩阵 void generateRandomMatrix(int** matrix, int rows, int columns) { srand(time(NULL)); for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { matrix[i][j] = rand() % 10; } } } // 释放矩阵内存 void deallocateMatrix(int** matrix, int rows) { for (int i = 0; i < rows; i++) { delete[] matrix[i]; } delete[] matrix; } // 串行矩阵乘法 int** multiplySerial(int** matrixA, int** matrixB, int rowsA, int colsA, int colsB) { int** result = new int*[rowsA]; for (int i = 0; i < rowsA; i++) { result[i] = new int[colsB]; for (int j = 0; j < colsB; j++) { result[i][j] = 0; for (int k = 0; k < colsA; k++) { result[i][j] += matrixA[i][k] * matrixB[k][j]; } } } return result; } // 并行矩阵乘法 int** multiplyParallel(int** matrixA, int** matrixB, int rowsA, int colsA, int colsB, int rank, int size) { int** result = new int*[rowsA]; for (int i = 0; i < rowsA; i++) { result[i] = new int[colsB]; for (int j = 0; j < colsB; j++) { result[i][j] = 0; } } int* colBuffer = new int[rowsA]; MPI_Status status; for (int k = 0; k < colsA; k++) { // 0进程将matrixA的第k列放入缓冲区,广播给所有进程 if (rank == 0) { for (int i = 0; i < rowsA; i++) { colBuffer[i] = matrixA[i][k]; } } MPI_Bcast(colBuffer, rowsA, MPI_INT, 0, MPI_COMM_WORLD); // 每个进程负责计算colsB中rank、rank+size、rank+2size...列 for (int j = rank; j < colsB; j += size) { for (int i = 0; i < rowsA; i++) { result[i][j] += colBuffer[i] * matrixB[k][j]; } } } delete[] colBuffer; return result; }
主函数
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 rowsA = 10; int colsA = 10; int colsB = 10; int** matrixA = nullptr; // 所有进程统一分配matrixB内存 int** matrixB = new int*[colsA]; for (int i = 0; i < colsA; i++) { matrixB[i] = new int[colsB]; } if (rank == 0) { matrixA = new int*[rowsA]; for (int i = 0; i < rowsA; i++) { matrixA[i] = new int[colsA]; } generateRandomMatrix(matrixA, rowsA, colsA); generateRandomMatrix(matrixB, colsA, colsB); } // 广播matrixB到所有进程 MPI_Bcast(&matrixB[0][0], colsA * colsB, MPI_INT, 0, MPI_COMM_WORLD); double serialTime = 0.0; int** serialResult = nullptr; // 仅0进程执行串行计算 if (rank == 0) { double startTime = MPI_Wtime(); serialResult = multiplySerial(matrixA, matrixB, rowsA, colsA, colsB); double endTime = MPI_Wtime(); serialTime = endTime - startTime; } // 所有进程执行并行计算 double startTime = MPI_Wtime(); int** parallelResult = multiplyParallel(matrixA, matrixB, rowsA, colsA, colsB, rank, size); double endTime = MPI_Wtime(); double parallelTime = endTime - startTime; // 合并并行结果到0进程 int* flatParallel = new int[rowsA * colsB]; if (rank == 0) { // 0进程先扁平化自身结果 for (int i = 0; i < rowsA; i++) { for (int j = 0; j < colsB; j++) { flatParallel[i * colsB + j] = parallelResult[i][j]; } } // 接收其他进程的结果并合并 for (int p = 1; p < size; p++) { int* tempFlat = new int[rowsA * colsB]; MPI_Recv(tempFlat, rowsA * colsB, MPI_INT, p, 0, MPI_COMM_WORLD, &status); for (int i = 0; i < rowsA; i++) { for (int j = p; j < colsB; j += size) { flatParallel[i * colsB + j] = tempFlat[i * colsB + j]; } } delete[] tempFlat; } // 将合并后的数据写回parallelResult for (int i = 0; i < rowsA; i++) { for (int j = 0; j < colsB; j++) { parallelResult[i][j] = flatParallel[i * colsB + j]; } } } else { // 非0进程扁平化自身结果并发送给0进程 for (int i = 0; i < rowsA; i++) { for (int j = 0; j < colsB; j++) { flatParallel[i * colsB + j] = parallelResult[i][j]; } } MPI_Send(flatParallel, rowsA * colsB, MPI_INT, 0, 0, MPI_COMM_WORLD); } delete[] flatParallel; // 对比串行与并行结果 bool isEqual = true; if (rank == 0) { for (int i = 0; i < rowsA; i++) { for (int j = 0; j < colsB; j++) { if (serialResult[i][j] != parallelResult[i][j]) { isEqual = false; break; } } if (!isEqual) break; } // 输出结果 cout << (isEqual ? "Results are equal." : "Results are not equal.") << endl; cout << "Serial Time: " << serialTime << " seconds" << endl; cout << "Parallel Time: " << parallelTime << " seconds" << endl; cout << "Speedup: " << serialTime / parallelTime << endl; cout << "Efficiency: " << (serialTime / parallelTime) / size << endl; deallocateMatrix(matrixA, rowsA); deallocateMatrix(serialResult, rowsA); } deallocateMatrix(matrixB, colsA); deallocateMatrix(parallelResult, rowsA); MPI_Finalize(); return 0; }
内容的提问来源于stack exchange,提问作者user22048545
相关产品推荐
相关产品推荐

