使用 MPI_Type_create_struct() 在 C 中传输包含动态数组的结构

Posted

技术标签:

【中文标题】使用 MPI_Type_create_struct() 在 C 中传输包含动态数组的结构【英文标题】:Using MPI_Type_create_struct() to transfer structs containing dynamic arrays in C 【发布时间】:2017-05-04 16:18:53 【问题描述】:

概述:我正在测试我使用 MPI_Type_create_struct() 创建的数据类型是否正确,因此会发送正确的值。我无法将存储在数组中的值传输到其他处理器。我认为这可能是每个结构绑定中数组的内存地址以及存储在用于创建数据类型 mpibound 的数组 indices[] 中的偏移量的问题。

问题: 我正在开发一个使用 MPI 的程序,我的最终目标是使用 MPI_Gatherv() 从下面声明的称为 bound 的结构数组中收集值。

struct bound 
   int      n;
   char*    name;
   double*  lat;
   double*  lon;
;

我创建了一个测试程序来确保我正确使用了 MPI_Type_create_struct()。我调用 MPI_Type_create_struct() 的函数写在下面。

 void CreateBoundType (struct bound a_bound) 
    int blocklens[4];              /*Block Lengths of data in structure*/
    MPI_Datatype old_types[4];     /*Data types of data in structure*/
    MPI_Aint indices[4];           /*Byte displacement of each piece of data*/
    MPI_Aint addr1, addr2, addr3, addr4, baseaddr;

   /*Set block lengths*/
   blocklens[0] = 1;
   blocklens[1] = 10;
   blocklens[2] = NPT_MAX;
   blocklens[3] = NPT_MAX;

   /*Set Data Types*/
   old_types[0] = MPI_INT;
   old_types[1] = MPI_CHAR;  
   old_types[2] = MPI_DOUBLE;  
   old_types[3] = MPI_DOUBLE;  

   /*Set byte displacement for each piece of data in structure*/
   /*!!!!!I expect that the following 8 lines cause my problem!!!!!!*/
   MPI_Get_address ( &a_bound,         &baseaddr);
   MPI_Get_address ( &a_bound.num_pts, &addr1);
   MPI_Get_address ( a_bound.label,    &addr2);
   MPI_Get_address ( a_bound.lat,      &addr3);
   MPI_Get_address ( a_bound.lon,      &addr4);
   indices[0] = addr1 - baseaddr;
   indices[1] = addr2 - baseaddr;
   indices[2] = addr3 - baseaddr;
   indices[3] = addr4 - baseaddr;

   /*Create structure type in MPI so that we can transfer boundaries between nodes*/
   MPI_Type_create_struct(4,blocklens,indices,old_types,&mpibound);
   MPI_Type_commit(&mpibound); 
   return;

当我尝试使用我创建的数据类型(它是一个全局变量,mpibound)时,在调用 MPI_Bcast() 时,存储在我使用的缓冲区结构的一部分的数组中的值不会更新,但是整数值 n(n 是数组的长度)在所有处理器上都会发生变化。因此,我认为我的问题与用于定义 mpibound 的偏移量(索引 [4])有关。

下面我编写了一个主函数,显示了我如何调用这个函数并设置结构。 (我省略了对 MPI_Init 和其他此类函数的调用,以使其尽可能短)

int main (int argc, char **argv) 

   /*Initialise MPI etc*/...

   /*Create structure to broadcast*/
   struct bound my_bound;
   my_bound.name  = strdup(string);
   my_bound.lat   = malloc(NPT_MAX*sizeof(double));
   my_bound.lon   = malloc(NPT_MAX*sizeof(double));
   if(rank == 0) 
      my_bound.n      = 5;
      my_bound.lat[0] = 2.6;
      my_bound.lon[0] = 4.2;
   

   /*Call the function that creates the type mpibound*/
   CreateBoundType(my_bound);

   /*Create buffer to be used in a Broadcast from the root processor (rank 0)*/
   struct bound *buff = malloc(sizeof(struct bound));
   buff->lat = malloc(NPT_MAX*sizeof(double));
   buff->lon = malloc(NPT_MAX*sizeof(double));
   buff = &my_bound;

   /*Cast values in buffer from proc 0 to all others*/
   MPI_Bcast(buff,1,mpibound,0,MPI_COMM_WORLD);

   /*Print values and checks, free memory etc*/...

   return(EXIT_SUCCESS);

在调用 MPi_Bcast 之后放置一些打印语句表明,在秩 >0 的 procs 上,n 的值从秩 0 更新为该广播,但 lat 和 lon 数组的第一个元素仍为 0。

如果你能帮助我,我非常感谢它已经为此苦苦挣扎了几天!我尽量保持简短,这是我能够创建的最佳版本。

感谢阅读!

【问题讨论】:

您的代码毫无意义。您为这两个数组分配了两个结构和一些内存,但随后您通过执行buff = &my_bound 完全丢弃了第二个数组的内存。无论如何,请准备一个适当的minimal reproducible example 以及更具体的预期输出和实际输出。 我支持@Zulan 的评论。你的代码充满了错误,我认为这不仅仅是一个 MPI 问题,它是你在这里错过的对 C 的基本理解。一旦你的 C 基础扎实了,你就可以通过尝试解决 MPI 来开始升级,但如果你想用 MPI + C 编程,C 语言绝对是先决条件。 【参考方案1】:

正如 Zulan 所指出的,有些事情在您的代码中毫无意义。但主要问题出在

void CreateBoundType (struct bound a_bound) 
//                    HERE HERE HERE HERE

您正在按值传递结构,这意味着 MPI 数据类型 mpibound 是从值副本的内存地址构造的。该副本包含labellatlon 的相同指针值,但基址将位于其他位置。因此,您不能使用该数据类型发送 main 中的结构实例,因为偏移量对其无效。

您应该做的是按地址传递结构。变化很小:

void CreateBoundType (struct bound *a_bound) 
   ...
   MPI_Get_address(a_bound,           &baseaddr);
   MPI_Get_address(&a_bound->n, &addr1);
   MPI_Get_address(a_bound->label,    &addr2);
   MPI_Get_address(a_bound->lat,      &addr3);
   MPI_Get_address(a_bound->lon,      &addr4);
   ...


...
/*Call the function that creates the type mpibound*/
CreateBoundType(&my_bound);
...

请注意,您将无法实现收集(v)此类结构的最终目标,因为偏移量仅对单个实例有效,而收集操作会创建数组。数组的每个元素可能具有不同的偏移量,因此需要单独的 MPI 数据类型。

既然您仍然为latlon 中的每一个分配全部内存,为什么不在结构中简单地使用数组?

struct bound 
   int     n;
   char    name[10];
   double  lat[NPT_MAX];
   double  lon[NPT_MAX];
;

不要忘记在创建 MPI 结构数据类型后使用MPI_Type_create_resized 将其大小调整为sizeof(struct bound)

另外,请注意,在 C 中,指向结构的指针是指向其第一个元素的指针,因此不必显式计算 n 的偏移量 - 语言保证它为 0。

【讨论】:

以上是关于使用 MPI_Type_create_struct() 在 C 中传输包含动态数组的结构的主要内容,如果未能解决你的问题,请参考以下文章

无法理解 MPI_Type_create_struct

使用 MPI 派生数据类型创建和通信“结构数组”

测试使用

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇