无法理解 MPI_Type_create_struct [英] Trouble Understanding MPI_Type_create_struct

查看:13
本文介绍了无法理解 MPI_Type_create_struct的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解 MPI_Type_create_struct 方法.假设我们有一个结构体:

I'm having trouble understanding the MPI_Type_create_struct method. Say we have a struct:

   struct foo(){
       float value;
       char rank;
   }

我们想将此结构发送到另一个进程.考虑下面的代码示例:

And we want to send this struct to another process. Conside the code sample below:

int count = 2; //number of elements in struct
MPI_Aint offsets[count] = {0, 8};
int blocklengths[count] = {1, 1};
MPI_Datatype types[count] = {MPI_FLOAT, MPI_CHAR};
MPI_Datatype my_mpi_type;

MPI_Type_create_struct(count, blocklengths, offsets, types, &my_mpi_type);

我不确定这个例子中的偏移量和块长度是做什么的.有人能解释一下上面这两部分吗?

I'm not sure what offsets and blocklengths do in this example. Can somebody explain these two parts above?

推荐答案

MPI_Type_create_struct() 的目的,如你所知,是提供一种创建用户的 MPI_Datatype 的方法>s 映射他的结构化类型.这些新类型随后将可用于 MPI 通信和其他调用,就像默认类型一样,例如允许以与传输 ints 或 float<数组相同的方式传输结构数组/code>s.

The purpose of MPI_Type_create_struct() is, as you know, to provide a way to create user's MPI_Datatypes mapping his structured types. These new types will subsequently be usable for MPI communications and other calls just as the default types, allowing for example to transfer arrays of structures the same way you would transfer arrays of ints or floats.

现在让我们更详细地了解函数本身.
以下是 man 命令返回的概要:

Now let's see the function itself in more details.
Here is its synopsis as returned by the man command:

NAME
   MPI_Type_create_struct -  Create an MPI datatype from a general set of
           datatypes, displacements, and block sizes

SYNOPSIS
   int MPI_Type_create_struct(int count,
                              const int array_of_blocklengths[],
                              const MPI_Aint array_of_displacements[],
                              const MPI_Datatype array_of_types[],
                              MPI_Datatype *newtype)

INPUT PARAMETERS
   count   - number  of  blocks  (integer)  ---  also number of entries
             in arrays array_of_types, array_of_displacements and
             array_of_blocklengths
   array_of_blocklengths
           - number of elements in each block (array of integer)
   array_of_displacements
           - byte displacement of each block (array of address integer)
   array_of_types
           - type of elements in each block (array of handles to datatype
             objects)

OUTPUT PARAMETERS
   newtype - new datatype (handle)

让我们看看输入参数的含义是否需要进一步解释:

Let's see for the input parameters if their meaning calls for further explanation:

  • count:这很清楚,在你的情况下,这将是 2
  • array_of_types:嗯,对于你的例子来说就是 { MPI_FLOAT, MPI_CHAR }
  • array_of_blocklengths:再说一遍,没什么好说的.{ 1, 1 } 正是你所需要的
  • array_of_displacements:这是一个你必须更加小心的地方.它对应于从结构开始到 array_of_types 中列出的每个元素的地址的内存地址偏移量.在您的情况下,这将类似于 { &f.value - &f, &f.rank - &f },其中 f 是类型foo.这里棘手的部分是,由于潜在的对齐限制,您不能确定这将等于 { 0, sizeof( float ) } (尽管在这里我很确定它会是).因此,使用如图所示的地址偏移量使该方法完全可移植.此外(thx Hristo Iliev 指向我)您可以(并且应该)使用 offsetof() 来自 stddef.h 的宏,它为您执行此指针运算,将代码简化为 { offsetof( foo, value ), offsetof( foo, rank ) }看起来更好.
  • count: this is quite clear, and in your case, that would be 2
  • array_of_types: well, that'd be { MPI_FLOAT, MPI_CHAR } for your example
  • array_of_blocklengths: again, not much to say. { 1, 1 } is what you need here
  • array_of_displacements: this is the one for which you have to be a bit more careful. It corresponds to the memory address offsets from the start of the structure, to the address of each element listed in array_of_types. In your case, that would be something like { &f.value - &f, &f.rank - &f }, with f being of type foo. The tricky part here is that, because of potential alignment constraints, you cannot be sure that this will be equal to { 0, sizeof( float ) } (although here I'm pretty sure it will be). Therefore, using addresses offsets as shown makes the method fully portable. Moreover (thx Hristo Iliev to pointing it to me) you can (and should) use the offsetof() macro from stddef.h which does exactly this pointer arithmetic for your, simplifying the code to { offsetof( foo, value ), offsetof( foo, rank ) } which looks nicer.

以这种方式初始化参数后,对MPI_Type_create_struct()的调用将返回一个新的MPI_Datatype,这将适用于发送或接收一个strong> foo 当时.原因是这种新类型没有考虑结构的实际范围,包括其字段的对齐约束.你的例子在这方面是完美的,因为它(很可能)是空洞的.

With the arguments initialised this way, the call to MPI_Type_create_struct() will return a new MPI_Datatype, which will be suitable for sending or receiving one foo at the time. The reason for that is that this new type doesn't take into account the actual extent of the structure, including the alignment constraints for its fields. And you example is perfect in this regards since it will (very likely) be hollow.

这样做的原因是 floats 通常有 32b 的对齐约束,而 chars 没有.因此,主题数组的第二个结构体foo的起始地址并不在第一个结构体的末尾.它位于下一个 32b 对齐的内存地址.这将在结构的一个元素的结尾到数组中下一个元素的开头之间留下一个 3 字节的空洞.

The reason for that is that floats have in general an alignment constraint of 32b, while chars have none. Therefore, the starting address of the second structure foo of an array of theme is not right at the end of the first one. It is at the next 32b-aligned memory address. This will leave us with a hole of 3 Bytes between the end of an element of the structure to the start of the next in the array.

要解决这个问题,您必须调整类型的大小以使用 MPI_Type_create_resized() 扩展它,其概要如下:

To handle this issue, you'll have to resize your type for extending it with MPI_Type_create_resized(), which synopsis is as follow:

NAME
   MPI_Type_create_resized -  Create a datatype with a new lower bound
        and extent from an existing datatype

SYNOPSIS
   int MPI_Type_create_resized(MPI_Datatype oldtype,
                               MPI_Aint lb,
                               MPI_Aint extent,
                               MPI_Datatype *newtype)

INPUT PARAMETERS
   oldtype - input datatype (handle)
   lb      - new lower bound of datatype (address integer)
   extent  - new extent of datatype (address integer)

OUTPUT PARAMETERS
   newtype - output datatype (handle)

使用它非常简单,因为 lbextend 都可以通过直接调用专门用于此目的的函数来检索,即 MPI_Type_get_extent()code>(但实际上,您也可以直接使用 0sizeof( foo )).另外,由于调用MPI_Type_get_extent()MPI_Type_create_resized()所使用的中间类型并没有在任何实际的MPI通信中使用,所以不需要提交MPI_Type_commit(),为您节省一些电话和时间.

Using it is quite easy as both lb and extend can be retrieved by directly calling a function specifically meant for this purpose, namely MPI_Type_get_extent() (but actually, you could also directly use 0 and sizeof( foo )). In addition, since the intermediary type used for calling MPI_Type_get_extent() and MPI_Type_create_resized() isn't used in any actual MPI communication, it doesn't need to be committed with MPI_Type_commit(), sparing you some calls and time.

现在,你的代码变成了:

Now, with that, your code becomes:

int count = 2;
int array_of_blocklengths[] = { 1, 1 };
MPI_Aint array_of_displacements[] = { offsetof( foo, value ),
                                      offsetof( foo, rank ) };
MPI_Datatype array_of_types[] = { MPI_FLOAT, MPI_CHAR };
MPI_Datatype tmp_type, my_mpi_type;
MPI_Aint lb, extent;

MPI_Type_create_struct( count, array_of_blocklengths, array_of_displacements,
                        array_of_types, &tmp_type );
MPI_Type_get_extent( tmp_type, &lb, &extent );
MPI_Type_create_resized( tmp_type, lb, extent, &my_mpi_type );
MPI_Type_commit( &my_mpi_type );

这篇关于无法理解 MPI_Type_create_struct的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆