MPI_Gatherv 没有正确收集数据

MPI_Gatherv is not collecting data correctly

我正在尝试使用 MPI_Gatherv 收集不同大小的数组,但出于某种原因,它只收集了第一个处理器中的第一个对象。当我执行上述 for 循环时,我从 xPos 和 yPos 获得了正确的值,但是当我将数据收集到 xFinal 和 yFinal 数组并打印出这些值时,我只获得了第一个 x 和 y。所以基本上第一个对象的 (x,y) 为 (0,0) 而我有 10 个对象,当它应该引用的实际对象具有不同的 (x, y)

为了以防万一,counts[rank] 和 displs 肯定是对的,因为我之前用它们来 scatterv

我是否错误地使用了 gatherrv?还是我打印不正确?

for ( a = 0; a < size; a++) {
    if (rank == a) {
        for ( i = 0 ; i < counts[rank]; i++) {
            printf("from procs %d: %lE %lE\n", rank, xPos[i], yPos[i]);
        }
    }
}


MPI_Gatherv(&xPos, counts[rank], MPI_DOUBLE, &xFinal, counts, displs, MPI_DOUBLE,0, MPI_COMM_WORLD);
MPI_Gatherv(&yPos, counts[rank], MPI_DOUBLE, &yFinal, counts, displs, MPI_DOUBLE,0, MPI_COMM_WORLD);


MPI_Finalize();

FILE* f = fopen("universe.out", "wt");
for (i = 0; i < N; i++)
    fprintf(f, "%lE %lE\n", xFinal[i], yFinal[i]);
fclose(f);

看来你是同时从各个行列写文件的。您应该将文件写入代码放在 if (rank == 0) { ... } 内,只让等级 0 写入:

if (rank == 0)
{
    FILE* f = fopen("universe.out", "wt");
    for (i = 0; i < N; i++)
        fprintf(f, "%lE %lE\n", xFinal[i], yFinal[i]);
    fclose(f);
}

否则文件的内容可以是任何内容。

以我一贯的MPI-IO倡导者的身份,请考虑MPI-IO解决此类问题。您可以通过让每个进程写入文件来完全跳过收集。此外,如果 N 很大,则不需要 N 个文件操作。让 MPI(直接或通过图书馆)让您的生活更轻松。

首先,基于文本的输出格式真的是您(和您的合作者)想要的吗?如果 universe.out 变得足够大以至于您想并行读取它,那么您将面临跨处理器分解文件的挑战。考虑并行 HDF5 (phdf5) 或 paralllel-netcdf (pnetcdf) 或任何其他具有自描述可移植文件格式的高级库。

这是一个示例,说明如何写出所有 x 值然后写出所有 y 值。

#include <stdio.h>
#include <mpi.h>
#include <unistd.h> //getpid
#include <stdlib.h> //srandom, random

#define MAX_PARTS 10

int main(int argc, char **argv) {
    int rank, nprocs;
    MPI_Offset nparts; // more than 2 billion particle possible
    int i;
    double *x, *y;
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Offset start=0, x_end;
    MPI_Info info;
    MPI_File fh;
    MPI_Status status;
    srandom(getpid());

    /* demonstrate this works even when particles are not evenly
 * distributed over the processes */
    nparts=((double)random()/RAND_MAX)*MAX_PARTS;

    x = malloc(nparts*sizeof(*x));
    y = malloc(nparts*sizeof(*y));
    for (i = 0; i< nparts; i++) {
        /* just some bogus data to see what happens */
        x[i] = rank*100+i;
        y[i] = rank*200+i;
    }

    /* not using this now. might tune later if needed */
    MPI_Info_create(&info);

    MPI_File_open(MPI_COMM_WORLD, "universe.out",
    MPI_MODE_CREATE|MPI_MODE_WRONLY, info,  &fh);
    MPI_File_set_view(fh, 0, MPI_DOUBLE, MPI_DOUBLE, "native", info);

    MPI_Scan(&nparts, &start, 1, MPI_OFFSET, MPI_SUM, MPI_COMM_WORLD);

    /* MPI_Scan is a prefix reduction: remove our contribution */
    x_end = start; /* only the last rank will use this in the bcast below */
    start -= nparts;

    MPI_Bcast(&x_end, 1, MPI_OFFSET, nprocs-1, MPI_COMM_WORLD);
    MPI_File_write_at_all(fh, start, x, nparts, MPI_DOUBLE, &status);
    MPI_File_write_at_all(fh, start+x_end, y, nparts, MPI_DOUBLE, &status);

    MPI_Info_free(&info);
    MPI_File_close(&fh);

    MPI_Finalize();

}

将 x 数组和 y 数组写成成对的 (x,y) 值是可能的,但是 有点复杂(你必须制作一个 MPI 数据类型)。

Parallel-NetCDF 有一个 "operation combining" 优化 你。 Parallel-HDF5 有一项 "multi-dataset i/o" 优化正在进行中 下一个版本。通过这些优化,您可以定义一个 3d 数组, 一维用于 x 和 y,第三维用于 "particle identifier"。然后你 可以 post x 值的操作,y 值的操作,以及 让图书馆将所有这些整合到一个调用中。