使用 MPI I/O 提取 C 结构的一部分

Using MPI I/O to extract a part of a C structure

我正在编写 C MPI 代码。它有一个结构:

typedef struct mystruct_s{
   double interesting_data;
   int boring_data;
   int trivial_data;
}mystruct;

现在,每个进程都有一个这些结构的数组,比方说

mystruct my_own_struct[M][N];

其中 M 和 N 是 "large",所有各种进程数组组成一个大数组 mystruct

现在,我需要将 interesting_data 值输出到文件中的单个数组中。正如我试图用变量名表示的那样,我不想输出结构的其他部分。我想使用 MPI I/O 来做到这一点。当然,我可以在每个进程中建立一个双数组并将 interesting_data 复制到其中并从那里获取它。但是,这会大大增加内存使用量,而且编码也不是非常优雅。我仍在学习 MPI I/O 和 MPI 派生数据类型。有没有一种方法可以使用它们 "extract" interesting_data 立即从结构数组中取出并输出到带有 MPI I/O 的大数组?

如果感兴趣的数据是单个 double 字段,那么您可以简单地创建一个 MPI_DOUBLE 的调整大小版本,其范围与结构相同:

MPI_Datatype newtype;
MPI_Type_create_resized(MPI_DOUBLE, 0L, sizeof(mystruct), &newtype);
MPI_Type_commit(&newtype);

现在,当提供该数据类型时,MPI 将采用一个 double 值,然后跳过结构的其余部分,从而到达下一个 double 值。等等。

您现在可以存储数据,例如:

MPI_File fh;
MPI_File_open(MPI_COMM_WORLD, "interesting_data.dat",
              MPI_MODE_CREATE | MPI_MODE_WRONLY,
              MPI_INFO_NULL, &fh);
MPI_File_set_view(fh, rank * M*N * sizeof(double), // possible integer overflow!!!
                  MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);
MPI_File_write(fh, &my_own_struct[0][0].interesting_data, M*N, newtype,
               MPI_STATUS_IGNORE);
MPI_File_close(&fh);

MPI_File_set_view 为每个级别定位文件指针并告诉 MPI 不执行任何二进制转换。然后 MPI_File_write 调用将写入合并 interesting_data 字段并将它们作为单个块写入文件视图指定的位置。即使 interesting_data 不是结构的第一个字段,它也会起作用。

如果interesting_data由多个结构字段组成,例如

typedef structure _foo
{
   int couldnt_care_less;
   double interesting_bar;
   int not_interesting;
   int interesting_baz;
   double less_interesting;
} foo;

首先应该创建一个结构类型:

MPI_Datatype stype;
int lens[2] = { 1, 1 };
MPI_Aint disps[2] = { offsetof(foo, interesting_bar), offsetof(foo, interesting_baz) };
MPI_Datatype types[2] = { MPI_DOUBLE, MPI_INT };
MPI_Type_create_struct(2, lens, disps, types, &stype);
// MPI_Type_commit(&stype) is not necessary

现在必须调整类型的大小,就像上面更简单的示例一样:

MPI_Datatype newtype;
MPI_Aint lb, extent;
MPI_Type_get_extent(stype, &lb, &extent);
MPI_Type_create_resized(stype, lb, sizeof(foo), &newtype);
MPI_Type_commit(&newtype);

此过程使新数据类型具有与 stype 相同的下限,这将简化写入调用。诀窍是正确设置文件视图。它需要创建另一种结构类型,由有趣的字段组成,它们之间没有间距(除非你想通过文件中的垃圾来浪费磁盘 space):

MPI_Datatype ftype;
int lens[2] = { 1, 1 };
MPI_Aint disps[2] = { 0, sizeof(double) };
MPI_Datatype types[2] = { MPI_DOUBLE, MPI_INT };
MPI_Type_create_struct(2, lens, disps, types, &ftype);
MPI_Type_commit(&ftype);

此数据类型随后用于设置文件视图:

// possible integer overflow!!!
MPI_Offset offset = rank * M*N * (sizeof(double) + sizeof(int));
MPI_File_set_view(fh, offset, ftype, ftype, "native", MPI_INFO_NULL);
MPI_File_write(fh, my_own_struct, M*N, newtype, MPI_STATUS_IGNORE);

这是可行的,因为 ftypenewtype 是一致的——它们由相同的基本元素(一个 double 和一个 int)组成,并且顺序相同.