使用MPI_Gather接收数据结构的正确方法

时间:2013-02-03 15:09:33

标签: c mpi

我有以下数据结构,我正在尝试使用MPI_Gather发送:

struct set {
    int nbits;
    char  bits[];
};

问题是我无法收集上述结构的所有项目,只收集第一项。其余的项目根本没有意义。

这是一个测试用例:

#include <stdio.h>
#include <stdlib.h>

#include "mpi.h"

#define SIZE 10

struct set {
    int nbits;
    char bits[];
};

int main(int argc, char *argv[]) {
    int np, rank, i;
    struct set *subsets, *single;
    void *buf;

    MPI_Init(&argc,&argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &np);

    single = malloc(sizeof(struct set) + SIZE);

    if(rank == 0) {
            subsets = malloc((sizeof(struct set) + SIZE) * np);
    }

    buf = &subsets[0];

    MPI_Datatype set_type, oldtypes[2];
    int blockcounts[2];
    MPI_Aint offsets[2];
    MPI_Aint addr[3];

    MPI_Get_address(single, &addr[0]);
    MPI_Get_address(&single->nbits, &addr[1]);
    MPI_Get_address(&single->bits, &addr[2]);

    offsets[0] = addr[1] - addr[0];
    oldtypes[0] = MPI_INT;
    blockcounts[0] = 1;

    offsets[1] = addr[2] - addr[0];
    oldtypes[1] = MPI_CHAR;
    blockcounts[1] = SIZE;

    MPI_Type_create_struct(2, blockcounts, offsets, oldtypes, &set_type);
    MPI_Type_commit(&set_type);

    single->nbits = 2;

    for(i=0; i<single->nbits; i++)
            single->bits[i] = 'A' + rank;

    MPI_Gather(single, 1, set_type, buf, 1, set_type, 0, MPI_COMM_WORLD);

    if(rank == 0) {
            void *ptr;
            struct set *fs;
            int size;

            MPI_Type_size(set_type, &size);

            ptr = buf;

            for(i=0; i<np; i++) {
                    size_t j;

                    fs = ptr;
                    printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);

                    for(j=0; j<2; j++)
                            printf("from rank %d: buf[%d] = %#x\n",
                                    i, j, fs->bits[j]);
                    ptr += size;
            }
    }

    MPI_Type_free(&set_type);

    MPI_Finalize();
}

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:0)

MPI中的问题并不像使用结构和MPI类型的指针算法那么多。

你有

        void *ptr;
        struct set *fs;
        int size;

        MPI_Type_size(set_type, &size);

        ptr = buf;

        for(i=0; i<np; i++) {
                size_t j;

                fs = ptr;
                printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);

                for(j=0; j<2; j++)
                        printf("from rank %d: buf[%d] = %#x\n",
                                i, j, fs->bits[j]);
                ptr += size;
        }
}

但是MPI_Type_size实际上给出了该类型的数据量;如果有填充(这可能会使字符数组在字边界上),这与sizeof不同。如果你想在这里使用MPI函数,如果你将一个函数调用切换到MPI_Type_extent,它实际上告诉你整个范围跨越了类型,你的代码就会运行......但是仍然存在问题。 / p>

如果你看看sizeof(struct set)+SIZEMPI_Type_extent()之间的区别,你会发现它们不一样;这样:

#define SIZE 10
struct set {
  int nbits
  char nbits[]
}

...

malloc(sizeof(struct set)+SIZE);

不同
struct set {
  int nbits
  char nbits[SIZE]
}

malloc(sizeof(struct set));

因为填充等问题。这意味着subsets的大小错误,并且在您拨打MPI_Gather时出现内存错误。

您可以通过几种不同的方式解决这个问题,但最简单的(也就行数最短)是使用已经调整大小的数组定义结构,然后使用数组索引而不是指针算法:

#include <stdio.h>
#include <stdlib.h>

#include "mpi.h"

#define SIZE 10

struct set {
    int nbits;
    char bits[SIZE];
};

int main(int argc, char *argv[]) {
    int np, rank, i;
    struct set *subsets, *single;

    MPI_Init(&argc,&argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &np);

    single = malloc(sizeof(struct set));

    if(rank == 0) {
            subsets = malloc(sizeof(struct set) * np);
    }

    MPI_Datatype set_type, oldtypes[2];
    int blockcounts[2];
    MPI_Aint offsets[2];
    MPI_Aint addr[3];

    MPI_Get_address(single, &addr[0]);
    MPI_Get_address(&single->nbits, &addr[1]);
    MPI_Get_address(&single->bits, &addr[2]);

    offsets[0] = addr[1] - addr[0];
    oldtypes[0] = MPI_INT;
    blockcounts[0] = 1;

    offsets[1] = addr[2] - addr[0];
    oldtypes[1] = MPI_CHAR;
    blockcounts[1] = SIZE;

    MPI_Type_create_struct(2, blockcounts, offsets, oldtypes, &set_type);
    MPI_Type_commit(&set_type);

    single->nbits = 2;

    for(i=0; i<single->nbits; i++)
            single->bits[i] = 'A' + rank;

    MPI_Gather(single, 1, set_type, &(subsets[0]), 1, set_type, 0, MPI_COMM_WORLD);

    if(rank == 0) {
            for(i=0; i<np; i++) {
                    struct set *fs = &(subsets[i]);
                    printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);

                    for(int j=0; j<2; j++)
                            printf("from rank %d: buf[%d] = %#x\n",
                                    i, j, fs->bits[j]);
            }
    }

    MPI_Type_free(&set_type);

    MPI_Finalize();
}

已更新以添加如果您无法执行此操作,只需更改缓冲区alloc的大小即可将数据收集到:

#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"

#define SIZE 10

struct set {
    int nbits;
    char bits[];
};

int main(int argc, char *argv[]) {
    int np, rank, i;
    struct set *single;
    void *buf;
    ptrdiff_t extent;

    MPI_Init(&argc,&argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &np);

    single = malloc(sizeof(struct set) + SIZE);

    MPI_Datatype set_type, oldtypes[2];
    int blockcounts[2];
    MPI_Aint offsets[2];
    MPI_Aint addr[3];

    MPI_Get_address(single, &addr[0]);
    MPI_Get_address(&single->nbits, &addr[1]);
    MPI_Get_address(&single->bits, &addr[2]);

    offsets[0] = addr[1] - addr[0];
    oldtypes[0] = MPI_INT;
    blockcounts[0] = 1;

    offsets[1] = addr[2] - addr[0];
    oldtypes[1] = MPI_CHAR;
    blockcounts[1] = SIZE;

    MPI_Type_create_struct(2, blockcounts, offsets, oldtypes, &set_type);
    MPI_Type_commit(&set_type);

    MPI_Type_extent(set_type, &extent);
    buf = malloc((int)extent * np);

    single->nbits = 2;

    for(i=0; i<single->nbits; i++)
            single->bits[i] = 'A' + rank;

    MPI_Gather(single, 1, set_type, buf, 1, set_type, 0, MPI_COMM_WORLD);

    if(rank == 0) {
            struct set *fs = buf;
            for(i=0; i<np; i++) {
                    printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);

                    for(int j=0; j<2; j++)
                            printf("from rank %d: buf[%d] = %#x\n",
                                    i, j, fs->bits[j]);

                    fs = (struct set *)((char *)fs + extent);
            }
    }

    MPI_Type_free(&set_type);

    MPI_Finalize();
}