MPI派生的数据类型适用于浮点数,但不适用于双精度数据库。这是对齐问题吗?

时间:2015-06-30 09:59:41

标签: c struct mpi memory-alignment

我有一个与C结构有关的奇怪问题,它是在MPI派生数据类型的帮助下进行通信的。以下示例有效;它只是发送一条消息,其中包含一个integer加上float个值。

Minmum工作示例:

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

int main(int argc, char *argv[]) {
    MPI_Init(&argc, &argv);

    int i, rank, tag = 1;
    MPI_Status status;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    // Array of doubles plus element count
    typedef struct {
        int row;
        float elements[4];
    } My_array;

    // Derived datatype for an array of doubles plus element count
    MPI_Datatype MY_ARRAY_TYPE;
    const int nr_blocks = 2;
    int blocklengths[2] = {1, 4};
    MPI_Datatype oldtypes[2] = {MPI_INT, MPI_FLOAT};
    MPI_Aint extent, lb;
    MPI_Type_get_extent(MPI_INT, &lb, &extent);
    MPI_Aint displacements[2] = {0, extent};
    MPI_Type_create_struct(nr_blocks, blocklengths, displacements,
                       oldtypes, &MY_ARRAY_TYPE);
    MPI_Type_commit(&MY_ARRAY_TYPE);

   if(rank == 0) {
        My_array array1  = {3, 3.1, 3.2, 3.3, 3.4};
        MPI_Send(&array1, 1, MY_ARRAY_TYPE, 1, tag, MPI_COMM_WORLD);
   }
   if(rank == 1) {
        My_array array2;
        MPI_Recv(&array2, 1, MY_ARRAY_TYPE, 0, tag, MPI_COMM_WORLD, &status);
        printf("Rank %d received elements of row %d:\n", rank, array2.row);
        for(i = 0; i < 4; i++)
            printf("\t%.1f\n", array2.elements[i]);
    }
    MPI_Type_free(&MY_ARRAY_TYPE);
    MPI_Finalize();
}

如果您可以访问MPI安装,则示例可以由mpicc -o example example.c编译并由mpirun -np 2 example运行。 输出应为

Rank 1 received elements of row 3:
    3.1
    3.2
    3.3
    3.4

问题: 现在,当float的数组更改为double s的数组,并相应地MPI_FLOAT更改为MPI_DOUBLE时,我得到了错误的结果。

此代码:

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

int main(int argc, char *argv[]) {
    MPI_Init(&argc, &argv);

    int i, rank, tag = 1;
    MPI_Status status;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    // Array of doubles plus element count
    typedef struct {
        int row;
        double elements[4];
    } My_array;

    // Derived datatype for an array of doubles plus element count
    MPI_Datatype MY_ARRAY_TYPE;
    const int nr_blocks = 2;
    int blocklengths[2] = {1, 4};
    MPI_Datatype oldtypes[2] = {MPI_INT, MPI_DOUBLE};
    MPI_Aint extent, lb;
    MPI_Type_get_extent(MPI_INT, &lb, &extent);
    MPI_Aint displacements[2] = {0, extent};
    MPI_Type_create_struct(nr_blocks, blocklengths, displacements,
                       oldtypes, &MY_ARRAY_TYPE);
    MPI_Type_commit(&MY_ARRAY_TYPE);

   if(rank == 0) {
        My_array array1  = {3, 3.1, 3.2, 3.3, 3.4};
        MPI_Send(&array1, 1, MY_ARRAY_TYPE, 1, tag, MPI_COMM_WORLD);
   }
   if(rank == 1) {
        My_array array2;
        MPI_Recv(&array2, 1, MY_ARRAY_TYPE, 0, tag, MPI_COMM_WORLD, &status);
        printf("Rank %d received elements of row %d:\n", rank, array2.row);
        for(i = 0; i < 4; i++)
            printf("\t%.1f\n", array2.elements[i]);
    }
    MPI_Type_free(&MY_ARRAY_TYPE);
    MPI_Finalize();
}

产生

Rank 1 received elements of row 3:
    3.1
    3.2
    3.3
    0.0

我尝试了一下,使用struct和派生数据类型中的其他数据(例如,整数数组而不只是一个,int / MPI_INT而不是float / MPI_FLOAT等)并且发现只有在使用双打时才会出现问题。这让我怀疑这可能是各种各样的对齐问题 - 但我被困在那里。 MPI应自动处理对齐。

问题:为什么上面的示例适用于float / MPI_FLOAT,而不适用于double / MPI_DOUBLE,我该如何修复它?

可能相关的一些机器细节:

  • CPU:AMD Opteron 6134
  • 地址大小:48位
  • 对齐:64
  • 编译:gcc 4.4.7
  • MPI库:(不幸的是)特定于供应商

编辑:正如Vladimir F的评论中所建议的那样,我添加了无效的代码。

1 个答案:

答案 0 :(得分:1)

我刚刚发现了问题所在:确实是对齐的。第二个代码清单正确产生前3个双精度只不过是一个奇怪的巧合......通过使用MPI_INT的扩展作为下一个值的偏移量,我假设没有填充。最好像这样计算偏移量:

#include <stddef.c> 
...
MPI_Datatype MY_ARRAY_TYPE;
const int nr_blocks = 2;
int blocklengths[2] = {1, 4};
MPI_Datatype oldtypes[2] = {MPI_INT, MPI_DOUBLE};
MPI_Aint displacements[2];
displacements[0] = offsetof(My_array, row);
displacements[1] = offsetof(My_array, elements);
MPI_Type_create_struct(nr_blocks, blocklengths, displacements,
                   oldtypes, &MY_ARRAY_TYPE);
MPI_Type_commit(&MY_ARRAY_TYPE);
...

我真的有兴趣看看它是如何以这种方式解决的...为什么我们得到3个正确的值和一个0.0?由于对齐关闭4个字节,双平均在我的平台上用8个字节表示,为什么我没有得到一些随机数?如果前3个中的每一个都取一个双倍的低4个字节加上下一个双倍的高4个,那么它们如何才能被正确解码?