c - MPI 共享内存和四精度的分段错误
问题描述
一个非常简单的使用 MPI 共享内存的 C 程序在__float128
与 GCC 一起使用时(但不与英特尔 C 编译器一起使用)对我来说会崩溃。该程序为每个 MPI 进程分配一个长一个元素的共享实数组(分配由主进程完成,然后由其他进程访问)。每个进程写入其中一个元素并将另一个元素打印到标准输出。当使用标准的内置类型float
和double
(或只是单个进程)时,根本没有问题。使用__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 中的一个问题。还是我在代码中遗漏了什么?
解决方案
这里发生的是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,在最近的英特尔处理器上,对齐和非对齐指令在数据对齐时以相同的速度运行,这可能是英特尔编译器不再生成对齐指令的原因。