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

使用MPI_Reduce的C++程序执行后出现挂起问题求助

MPI程序无法正常结束的解决方案

我编写了一个C++ MPI程序,让每个进程生成一个数组,然后用MPI_Reduce统计对应位置元素的和。程序能正常输出结果,但无法正常结束,必须用Ctrl+C终止,看起来有进程在等待操作。代码如下:

#include <iostream>
#include <vector>
#include "mpi.h"

int ROOT = 0;

int main(int argc, char *argv[]) {
    int procNum, procRank;

    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &procNum);
    MPI_Comm_rank(MPI_COMM_WORLD, &procRank);

    int N = procNum + 5;
    int* a = new int[N];

    srand(procRank);
    std::cout << "Process #" << procRank << " generated array:\n";
    for (int i = 0; i < N; i++) {
        a[i] = rand() % 10;
        std::cout << a[i] << " ";
    }
    std::cout << "\n";

    int* sendbuf = new int [N];
    int* recvbuf = new int [N];

    for (int i = 0; i < N; i++)
        sendbuf[i] = a[i];

    MPI_Reduce(sendbuf, recvbuf, N, MPI_INT, MPI_SUM, ROOT, MPI_COMM_WORLD);

    if (procRank == 0) {
        std::cout << "Process #0 received sums:\n";
        for (int i = 0; i < N; i++) {
            std::cout << "Sum #" << i << ": " << recvbuf[i] << "\t";
        }
    }

    delete[] a;
    delete[] recvbuf;
    delete[] sendbuf;

    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Finalize();
}

问题原因

程序死锁的核心原因是root进程的标准输出缓冲未及时刷新:root进程输出结果的循环结束后没有添加换行符,std::cout的行缓冲机制会把输出内容滞留在缓冲区中,导致root进程无法继续执行后续的MPI_Barrier和MPI_Finalize;而非root进程已经到达MPI_Barrier等待所有进程同步,最终形成死锁。

另外,非root进程分配recvbuf完全多余,MPI_Reduce仅要求root进程提供有效的接收缓冲区,其他进程的recvbuf参数可以忽略(设为NULL即可)。

修改后的代码

#include <iostream>
#include <vector>
#include "mpi.h"

int ROOT = 0;

int main(int argc, char *argv[]) {
    int procNum, procRank;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &procNum);
    MPI_Comm_rank(MPI_COMM_WORLD, &procRank);

    int N = procNum + 5;
    int* a = new int[N];

    srand(procRank);
    std::cout << "Process #" << procRank << " generated array:\n";
    for (int i = 0; i < N; i++) {
        a[i] = rand() % 10;
        std::cout << a[i] << " ";
    }
    std::cout << "\n";
    std::cout.flush(); // 强制刷新输出缓冲,避免阻塞

    // 直接复用a作为发送缓冲区,无需额外分配内存
    int* sendbuf = a;
    int* recvbuf = nullptr;
    // 仅root进程分配接收缓冲区
    if (procRank == ROOT) {
        recvbuf = new int[N];
    }

    MPI_Reduce(sendbuf, recvbuf, N, MPI_INT, MPI_SUM, ROOT, MPI_COMM_WORLD);

    if (procRank == ROOT) {
        std::cout << "Process #0 received sums:\n";
        for (int i = 0; i < N; i++) {
            std::cout << "Sum #" << i << ": " << recvbuf[i] << "\t";
        }
        std::cout << "\n"; // 添加换行符,触发行缓冲自动刷新
        std::cout.flush();
    }

    // 释放内存,仅root进程需要释放recvbuf
    delete[] a;
    if (procRank == ROOT) {
        delete[] recvbuf;
    }

    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Finalize();

    return 0;
}

关键修改点

  • 在所有进程的输出后添加std::cout.flush(),强制刷新输出缓冲,避免进程因等待缓冲写入而阻塞。
  • root进程的结果输出末尾添加换行符,确保行缓冲及时刷新,让进程能顺利进入后续同步操作。
  • 非root进程不再分配recvbuf,仅root进程按需分配,减少不必要的内存开销。
  • 直接复用生成的数组a作为发送缓冲区,省去额外的内存拷贝和分配步骤。

内容的提问来源于stack exchange,提问作者randomsquare

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 07:01:16