You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MPI的C++矩阵乘法代码问题:结果不一致与多进程崩溃

矩阵乘法并行程序的问题排查与修复

问题1:串行与并行计算结果不一致

原因分析

  1. 累加逻辑错误:并行函数中每次处理矩阵A的一列时,都会将result[i][j]重置为0再重新计算,而矩阵乘法的正确逻辑是对每一轮乘积进行累加,覆盖式赋值导致最终结果仅保留最后一轮计算值。
  2. 变量名冲突:并行函数外层循环变量为k,内层点积循环变量也用k,导致循环逻辑混乱,变量值被意外覆盖。
  3. 并行结果未合并:原代码中每个进程仅计算部分列,但未将各进程的计算结果汇总到0进程,导致0进程的并行结果不完整,对比时出现不一致。

修复步骤

  • 删除并行函数中result[i][j] = 0;代码(结果矩阵初始化时已设为0,后续只需累加)。
  • 将内层点积循环变量名从k改为m,避免与外层循环变量冲突。
  • 新增并行结果合并逻辑:将各进程计算的部分列数据汇总到0进程,形成完整的乘积矩阵。

问题2:多进程运行崩溃

原因分析

  1. 空指针访问:非0进程的matrixA和matrixB未分配内存,直接传递给计算函数或MPI_Bcast会触发内存访问错误。
  2. 无效函数调用:非0进程没有完整的matrixA数据,却调用multiplySerial函数,导致空指针解引用崩溃。
  3. MPI通信逻辑错误:原代码使用Allgather传递矩阵A的列数据,但只有0进程持有完整的matrixA,非0进程无有效数据可发送,引发通信错误。

修复步骤

  1. 统一分配matrixB内存:所有进程都为matrixB分配内存后,再执行MPI_Bcast接收数据。
  2. 限制串行计算范围:仅0进程执行串行计算,非0进程跳过该步骤,避免空指针访问。
  3. 修正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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 00:27:00