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

Posted

技术标签:

【中文标题】MPI 派生数据类型适用于浮点数,但不适用于双精度数。是对齐问题吗?【英文标题】:MPI derived datatype works for floats, but not for doubles. Is it an alignment issue? 【发布时间】:2015-09-17 01:36:30 【问题描述】:

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

最小工作示例:

#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

问题: 现在,当floats 的数组更改为doubles 的数组,并相应地将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

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

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

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

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

编辑:按照 Vladimir F 在 cmets 中的建议,我添加了不起作用的代码。

【问题讨论】:

最好显示导致错误的代码,以避免在更改和测试时出现任何可能的不确定性。当您不显示导致代码的确切错误时,可能会隐藏一个微妙的问题。 尝试 printf-ing doubles with lf not f... 【参考方案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 个字节分别取一个 double 的低 4 个字节加上下一个 double 的高 4 个字节,怎么能正确解码?

【讨论】:

双精度中的高阶位为零,不是吗?尝试设置为 MAX_DBL (?) 看看是否会改变结果。

以上是关于MPI 派生数据类型适用于浮点数,但不适用于双精度数。是对齐问题吗?的主要内容,如果未能解决你的问题,请参考以下文章

MPI 代码不适用于 2 个节点,但适用于 1 个节点

如何将模数用于浮点/双精度?

使用 AVX 的有符号/无符号整数的最小值

精度浮点型数据精确到了几位小数呢?

为啥 F# 中的幂运算符仅适用于浮点数?

将列表中的所有字符串转换为浮点数。适用于单个列表,但不适用于数据框