0

MPI2 允许我们创建派生数据类型并通过编写发送它们

call mpi_type_create_indexed_block(size,1,dspl_send,rtype,DerType,ierr)
call mpi_send(data,1,DerType,jRank,20,comm,ierr)

通过这样做,数据(N)的位置dspl_sendMPI库发送。

现在,对于矩阵data(M,N),我们可以通过以下代码发送它的位置:

call mpi_type_create_indexed_block(size,M,dspl_send,rtype,DerTypeM,ierr)
call mpi_send(data,1,DerTypeM,jRank,20,comm,ierr)

即发送条目data(i, dspl_send(j))

我的问题是关于 1 在随后的 mpi_send 中的作用。它必须始终为 1 吗?其他尺寸可能吗?MPI 派生的数据类型在 Internet 上的许多文档中都有很好的解释,但是 send/recv 中的大小始终为1,而没有提及是否允许其他大小以及如何使用它。

如果我们想使用大小 M 在调用之间变化的矩阵data(M,N),我们是否需要在调用时始终创建派生数据类型?是否无法使用DerType发送矩阵data(M,N)data(N,M)

4

2 回答 2

3

每个 MPI 数据类型都有两个属性:大小和范围。大小是数据类型表示的实际字节数,而范围是数据类型在内存中覆盖的字节数。某些数据类型不是连续的,这意味着它们的大小可能小于它们的范围,例如(此处以伪代码显示)

MPI_TYPE_VECTOR(count = 1,
                blocklength = 10,
                stride = 20,
                oldtype = MPI_INTEGER,
                newtype = newtype)

创建一个数据类型,该数据类型blocklength从总共 20 个 ( ) 中获取前 10 个 ( ) 元素stride。此数据类型的大小是大多数系统上以字节计的大小的10倍数。在大多数系统上,它的范围是两倍或字节。如果是 2,那么它将采用 10 个元素,然后跳过接下来的 10 个,然后再采用另外 10 个元素并再次跳过接下来的 10 个。因此它的大小和它的扩展将是两倍大。MPI_INTEGER4080count

当您在任何 MPI 例程中指定某个元素计数时,例如MPI_SEND,MPI 会执行以下操作:

  1. 它使用源缓冲区参数的地址初始化内部数据缓冲区。
  2. 它咨询数据类型类型映射以确定多少字节以及从何处获取并将它们附加到正在构造的消息中。添加的字节数等于数据类型的大小
  3. 它按数据类型的范围递增内部数据指针。
  4. 它减少内部计数,如果它仍然非零,则重复前两个步骤。

MPI 的一个很好的特性是数据类型的范围不需要匹配其大小(如矢量示例中所示),甚至可以使用MPI_TYPE_CREATE_RESIZED. 这允许创建非常复杂的数据访问模式。例如,使用MPI_SCATTERV不跨越整行 (C) 或整列 (Fortran) 的块来分散矩阵需要使用这种调整大小的类型。

回到向量示例。无论是使用创建向量类型count = 1然后调用MPI_SEND使用,count = 2还是使用创建向量类型count = 2然后调用MPI_SEND使用count = 1,最终结果都是相同的。通常人们会构造一种数据类型来完全描述想要发送的对象。在这种情况下,人们会count = 1调用MPI_SEND. 但是在某些情况下,创建只描述对象的一部分(例如单个部分)的数据类型可能更有益,然后MPI_SEND使用countset 调用要发送的部分数量。有时是个人喜好问题,有时是算法要求问题。

至于你的最后一个问题,Fortran 以列优先顺序存储矩阵,这意味着它data(i,j)data(i±1,j)内存中,而不是在data(i,j±1). 因此,data(M,N)由每个元素的N连续列向量M组成。data(1,1)例如,两个元素之间的距离data(1,2)取决于M。这就是您M在类型构造函数中提供的原因。具有不同行数(例如不同M)的矩阵不会“适合”所创建类型的类型映射,并且会使用错误的元素来构造消息。

于 2012-12-10T13:47:53.680 回答
0

https://stackoverflow.com/a/13802243/7784768中关于范围的描述并不完全正确,因为范围没有考虑数据类型末尾的填充。MPI 数据类型由 typemap 定义:

typemap = ((type_0, disp_0 ), ..., (type_n−1, disp_n−1 ))

然后根据以下定义范围

lb = min(disp_j)
ub = max(disp_j + sizeof(type_j)) + e)
extent = ub - lb,

其中,由于对齐要求,e 可以是非零值。

这意味着在示例中

MPI_TYPE_VECTOR(count = 1,
                blocklength = 10,
                stride = 20,
                oldtype = MPI_INTEGER,
                newtype = newtype)

当 count=1 时,typemap 是

((int, 0), (int, 4), ... (int, 36))

在大多数系统中,范围是 40 而不是 80(即在这种情况下,步幅对类型映射没有影响)。对于 count=2,typemap 将是

((int, 0), (int, 4), ... (int, 36), (int, 80), (int, 84), ... (int, 116))

和范围 120(第一个 10 个整数块为 40 个字节,步幅为 40 个字节,第二个 10 个整数块为 40 个字节,但剩余步幅在范围中被忽略)。使用 MPI_Type_get_extent 函数可以轻松找出范围。

范围是一个非常棘手的概念,在尝试传达派生数据类型的多个元素时很容易出错。

于 2017-03-29T09:46:36.897 回答