首页 > 解决方案 > MPI 共享内存和四精度的分段错误

问题描述

一个非常简单的使用 MPI 共享内存的 C 程序在__float128与 GCC 一起使用时(但不与英特尔 C 编译器一起使用)对我来说会崩溃。该程序为每个 MPI 进程分配一个长一个元素的共享实数组(分配由主进程完成,然后由其他进程访问)。每个进程写入其中一个元素并将另一个元素打印到标准输出。当使用标准的内置类型floatdouble(或只是单个进程)时,根本没有问题。使用__float128(和多个进程)时,下面突出显示的行会导致所有进程出现分段错误。

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

const int verbose = 0;

/* typedef float real; */
/* typedef double real; */
typedef __float128 real;

#define _(FUN, ARGS) \
{ \
    int status = FUN ARGS; \
    if (verbose) printf(#FUN " exit code: %d\n", status); \
}

int main (int argc, char* argv[])
{
    int nprocs, rank, assert = 0, disp = 1, master = 0, bytesize = sizeof(real);
    void* baseptr = NULL;
    real* arr;
    MPI_Win win;

    printf("Data type byte size: %d\n", bytesize);

    _(MPI_Init,      (&argc, &argv))
    _(MPI_Comm_size, (MPI_COMM_WORLD, &nprocs))
    _(MPI_Comm_rank, (MPI_COMM_WORLD, &rank))

    MPI_Aint totsize = rank == 0 ? bytesize * nprocs : 0;

    _(MPI_Win_allocate_shared, (totsize, disp, MPI_INFO_NULL, MPI_COMM_WORLD, &baseptr, &win))
    _(MPI_Win_shared_query,    (win, master, &totsize, &disp, &arr))
    _(MPI_Win_fence,           (assert, win))

    arr[rank] = rank + 1;  /*  <-- Segmentation fault here when using __float128 */

    _(MPI_Win_fence, (assert, win))

    printf("Element %d: %g\n", (rank + 1) % nprocs, (double)arr[(rank + 1) % nprocs]);

    _(MPI_Win_fence, (assert, win))
    _(MPI_Win_free,  (&win));
    _(MPI_Finalize,  ());

    return 0;
}

该程序适用于某些配置,但不适用于其他配置。以下是我能够检查的内容:

编译器 打开 MPI 4.1.0 英特尔 MPI(oneAPI 2021)
英特尔 oneAPI 2021mpiicc 适用于所有类型 适用于所有类型
海合会 9.3mpicc 失败__float128 不适用
海合会 10.2mpicc 失败__float128 不适用

我使用 openSUSE Tumbleweed 20210319。

乍一看,这似乎是 GCC 中的一个问题。还是我在代码中遗漏了什么?

标签: cgccmpiintelshared-memory

解决方案


这里发生的是MPI_Win_allocate_shared()返回一个按 8 字节对齐的内存区域,但 GCC 假设按 16 字节对齐,如果未按 16 字节对齐,arr可能会发生崩溃。arr

您可以通过手动重新对齐数据来解决此问题。

#define REALIGN(a, type) \
    ((a) + sizeof(type) - 1 & ~(sizeof(type) - 1))

接着

    MPI_Aint totsize = rank == 0 ? (bytesize * (nprocs+1)) : 0;

最后

    arr = (real *)REALIGN((unsigned long)arr, real);

FWIW,在最近的英特尔处理器上,对齐和非对齐指令在数据对齐时以相同的速度运行,这可能是英特尔编译器不再生成对齐指令的原因。


推荐阅读