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

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

Minmum工作示例:

 #include  #include  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_FLOATMPI_DOUBLE ,我得到了错误的结果。

这段代码:

 #include  #include  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_FLOAT而不是float / MPI_FLOAT等),并且看到问题出现时才会出现双精度问题用过的。 这让我怀疑这可能是各种各样的对齐问题 – 但我被困在那里。 MPI应自动处理对齐。

问题:为什么上面的例子适用于float / MPI_FLOAT ,而不是double / MPI_DOUBLE ,我该如何解决?

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

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

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

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

 #include  ... 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个字节表示,为什么我没有得到一些随机数? 如果前三个分别取一个双4的低4个加上下一个双4的高4个,那么它们如何正确解码?