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

Posted

技术标签:

【中文标题】使用 MPI 派生数据类型创建和通信“结构数组”【英文标题】:Create and communicate an "array of structs" using MPI Derived datatypes 【发布时间】:2016-10-18 16:07:35 【问题描述】:

我正在尝试使用 MPI_Type_create_struct 使用 MPI 派生数据类型对 MPI_Alltoallv 进行编程。我找不到任何解决这个特定问题的例子。大多数示例like this 使用单个结构成员执行通信(发送/接收),而我的目标是一个结构数组。以下是一个更简单的测试代码,它尝试对使用 DDT 创建的结构数组执行 MPI_Sendrecv 操作:

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

typedef struct sample
  char str[12];
  int  count;
my_struct;

int main(int argc, char **argv)

    int rank, count;
    my_struct *sbuf = (my_struct *) calloc (sizeof(my_struct),5);
    my_struct *rbuf = (my_struct *) calloc (sizeof(my_struct),5);
    int blens[2];
    MPI_Aint displs[2];
    MPI_Aint baseaddr, addr1, addr2;
    MPI_Datatype types[2];
    MPI_Datatype contigs[5];
    MPI_Status status;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    strcpy(sbuf[0].str,"ACTGCCAATTCG");
    sbuf[0].count = 10;
    strcpy(sbuf[1].str,"ACTGCCCATACG");
    sbuf[1].count = 5;
    strcpy(sbuf[2].str,"ACTGCCAATTTT");
    sbuf[2].count = 6;
    strcpy(sbuf[3].str,"CCTCCCAATTCG");
    sbuf[3].count = 12;
    strcpy(sbuf[4].str,"ACTATGAATTCG");
    sbuf[4].count = 8;

    blens[0] = 12; blens[1] = 1;
    types[0]  = MPI_CHAR; types[1]  = MPI_INT;
    for (int i=0; i<5; i++)
    
       MPI_Get_address ( &sbuf[i], &baseaddr);
       MPI_Get_address ( &sbuf[i].str, &addr1);
       MPI_Get_address ( &sbuf[i].count, &addr2);
       displs[0] = addr1 - baseaddr;
       displs[1] = addr2 - baseaddr;

       MPI_Type_create_struct(2, blens, displs, types, &contigs[i]);
       MPI_Type_commit(&contigs[i]);
      

    /* send to ourself */
     MPI_Sendrecv(sbuf, 5, contigs, 0, 0,
             rbuf, 5, contigs, 0, 0,
             MPI_COMM_SELF, &status);

     for (int i=0; i<5; i++)
          MPI_Type_free(&contigs[i]);

     MPI_Finalize();

     return 0;
 

我在编译时收到以下警告:

    coll.c(53): warning #810: conversion from "MPI_Datatype=int *" to "MPI_Datatype=int" may lose significant bits
       MPI_Sendrecv(sbuf, 5, contigs, 0, 0,
                             ^

    coll.c(54): warning #810: conversion from "MPI_Datatype=int *" to "MPI_Datatype=int" may lose significant bits
               rbuf, 5, contigs, 0, 0,

并在所有进程中观察以下错误:

    Rank 0 [Thu Jun 16 16:19:24 2016] [c0-0c2s9n1] Fatal error in MPI_Sendrecv: Invalid datatype, error stack:
    MPI_Sendrecv(232): MPI_Sendrecv(sbuf=0x9ac440, scount=5, INVALID DATATYPE, dest=0, stag=0, rbuf=0x9ac4a0, rcount=5, INVALID DATATYPE, src=0, rtag=0, MPI_COMM_SELF, status=0x7fffffff6780) failed

不知道我做错了什么。我是否需要进一步使用“MPI_Type_create_resized”来注册“范围”?如果是这样,引用上述场景的示例将非常有帮助。

另外,我的主要目标是使用类似的结构数组(大小约为数千)来执行“MPI_Alltoallv”。希望如果我能让 SendRecv 工作,我可以继续使用“MPI_Alltoallv”。

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

sendtyperecvtype 参数需要 MPI_Datatype 类型的参数。你传入的是一个数组,即MPI_Datatype *

您一次只能使用其中一个数组元素来传递给此函数。

【讨论】:

我也是这么想的。但是如果我只传递一个“contigs”实例,那么接收端将如何接收剩余 4 个条目的位移?我不需要发送“contigs”数据类型的所有实例吗? 看起来所有 5 个条目都需要有一个共同的发送/接收类型。如果没有,您需要一次发送一份。 您的所有条目都是同一类型 - 它们都是 my_struct 类型。您只需要定义一个 MPI_Datatype,它对于所有结构都是相同的。如果您在 Sendrecv 调用中为这两种类型指定 contigs[0],则代码编译并运行正常(尽管我不知道它是否正确)。关键是结构定义中的位移都是相对的,而且由于 C 保证结构的所有实例具有相同的布局,因此您只需要定义一个新的数据类型。

以上是关于使用 MPI 派生数据类型创建和通信“结构数组”的主要内容,如果未能解决你的问题,请参考以下文章

结构填充和非阻塞通信缓冲区问题导致的 MPI 派生数据类型问题

c ++ mpi在不同机器上派生数据类型

具有动态分配成员的动态分配结构的 MPI 派生数据类型

使用 MPI/OpenMP 的具有派生数据类型(嵌套类对象)容器的 C++ 程序

C ++ MPI创建并发送具有字段char [16]和整数的结构数组

C++ MPI 创建并发送具有字段 char[16] 和 integer 的结构数组