首页 > 解决方案 > 使用非阻塞操作计算数组的总和

问题描述

每个进程都需要计算它的部分和并将它们发送到 0 进程,然后计算数组的总和我写了这段代码

    double* a;
    a = new double[N];
    for (int i = 0; i < N; i++)
        a[i] = 1.0;
    int k = (N - 1) / proc_count + 1;
    int ibeg = proc_this * k;
    int iend = (proc_this + 1) * k - 1;
    if (ibeg >= N)
        iend = ibeg - 1;
    else if(iend >= N)
        iend = N - 1;
    double s = 0;
    for (int i = ibeg; i <= iend; i++)
        s += a[i];
    MPI_Status* stats = new MPI_Status[proc_count];
    MPI_Request* reqs = new MPI_Request[proc_count];
    double* inmes = new double[proc_count];
    inmes[0] = s;
    if (proc_this != 0)
        MPI_Isend(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &reqs[proc_this]);
    else
        for (int i = 1; i < proc_count; i++) 
            MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);
    MPI_Waitall(proc_count, reqs, stats);
    MPI_Finalize();
    if (proc_this == 0) {
        for (int i = 1; i<proc_count; i++)
            inmes[0] += inmes[i];
        printf("sum = %f", inmes[0]);
    }
    delete[] a;

但它不断给出错误

Fatal error in PMPI_Waitall: Invalid MPI_Request, error stack:
PMPI_Waitall(274): MPI_Waitall(count=1, req_array=00000212B7B24A40, status_array=00000212B7B34740) failed
PMPI_Waitall(250): The supplied request in array element 0 was invalid (kind=3)

你能解释一下我做错了什么吗?

标签: c++mpimpich

解决方案


简而言之,您需要在分配后将所有元素设置reqs为正确。MPI_REQUEST_NULL

更长的答案是 MPI 程序作为一个或多个源程序的多个实例运行,并且每个实例(等级)都有自己的一组不共享的变量。当你有:

    if (proc_this != 0)
        MPI_Isend(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &reqs[proc_this]);
    else
        for (int i = 1; i < proc_count; i++) 
            MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);

您期望结果将reqs是充满价值的:

reqs: [ Irecv req | Isend req 1 | Isend req 2 | ... ]

现实情况是,您将拥有:

reqs in rank 0: [ Irecv req |    ???    |    ???    | ... ??? ... ]
reqs in rank 1: [    ???    | Isend req |    ???    | ... ??? ... ]
reqs in rank 2: [    ???    |    ???    | Isend req | ... ??? ... ]
etc.

where???代表未初始化的内存。MPI_Waitall()是本地操作,它只看到reqs. 它无法完成其他等级发布的请求。

未初始化的内存可以有任何值,如果该值导致无效的请求句柄,MPI_Waitall()将中止并出现错误。如果将所有请求设置为MPI_REQUEST_NULL,则不会发生这种情况,因为会忽略空请求。

您的代码中还存在语义错误:

MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);

将所有接收请求存储在同一位置,每个新请求都会覆盖前一个请求。因此,除了最后一个请求之外,您将永远无法等待任何请求。


鉴于您在MPI_Waitall()之后立即调用MPI_Isend(),因此使用非阻塞发送毫无意义。更简洁的代码版本是:

    if (proc_this != 0)
        MPI_Send(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
    else {
        MPI_Request* reqs = new MPI_Request[proc_count - 1];
        for (int i = 1; i < proc_count; i++) 
            MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[i-1]);
        MPI_Waitall(proc_count-1, reqs, MPI_STATUSES_IGNORE);
        delete [] reqs;
    }

推荐阅读