MPI数据类型用于二维数组

5

我需要从根节点向所有处理器传递一个整数数组的数组(基本上是一个二维数组)。我正在使用C程序中的MPI。如何声明MPI数据类型以用于二维数组,以及如何发送消息(应该使用广播还是散射)。

4个回答

13

你需要使用Broadcast,因为你想要将相同的消息副本发送到每个进程。Scatter会将消息分解并在进程之间分配块。

至于如何发送数据:HIndexed数据类型适合你。

假设你的二维数组定义如下:

int N;            // number of arrays (first dimension)
int sizes[N];     // number of elements in each array (second dimensions)
int* arrays[N];   // pointers to the start of each array

首先,您需要计算每个数组的起始地址相对于数据类型的起始地址的位移量,这可以是第一个数组的起始地址,以方便处理:

MPI_Aint base;
MPI_Address(arrays[0], &base);
MPI_Aint* displacements = new int[N];
for (int i=0; i<N; ++i)
{
    MPI_Address(arrays[i], &displacements[i]);
    displacements[i] -= base;
}

那么你的类型的定义将会是:
MPI_Datatype newType;
MPI_Type_hindexed(N, sizes, displacements, MPI_INTEGER, &newType);
MPI_Type_commit(&newType);

这个定义将创建一个数据类型,其中包含所有数组按顺序打包在一起。完成后,您只需将数据作为此类型的单个对象发送:

MPI_Bcast(arrays, 1, newType, root, comm);   // 'root' and 'comm' is whatever you need

然而,你还没有完成。接收方进程需要知道要发送的数组的大小:如果这些信息在编译时不可用,则必须首先发送包含该数据的单独消息(int数组)。如果在接收方进程上定义了类似上述的Nsizesarrays,并已分配足够的空间来填充这些数组,则所有接收方进程只需要定义相同的数据类型(与发送方完全相同的代码),然后将发送方的消息作为该类型的单个实例进行接收:

MPI_Bcast(arrays, 1, newType, root, comm);    // 'root' and 'comm' must have the same value as in the sender's code

看,现在所有的进程都有你的数组的副本了。

当然,如果你的二维数组的第二个维度固定为某个值M,那么事情会变得更加容易。在这种情况下,最简单的解决方案是将其存储在一个单独的int[N*M]数组中:C++将保证它是连续的内存,因此您可以在不定义自定义数据类型的情况下进行广播,例如:

MPI_Bcast(arrays, N*M, MPI_INTEGER, root, comm);

注意: 你可能可以使用Indexed类型代替HIndexed。区别在于,在Indexed中,displacements数组以元素数量给出,而在HIndexed中是以字节数给出(H代表异构)。如果你要使用Indexed,则displacements中给出的值必须除以sizeof(int)。但是,我不确定在堆上任意位置定义的整数数组是否保证在C++中“排列”到整数极限,无论如何,HIndexed版本具有(稍微)少一些代码并产生相同的结果。

@pyCthon: 不会的,Scatter只会将一个数组分割成几个块,但是你这里只发送了一个对象(具有复杂的类型)。再次,你需要问自己,将一个具有不同第二维度的二维数组进行分散是否有意义。 - suszterpatt
是的,我只是好奇它是否能够工作。标准的做法是使用块分解方法来划分工作和问题规模。 - pyCthon
刚找到了一个非常好的 MPI 派生数据类型资源,其中包含可工作的代码示例:链接 - tomuś

4
如果您正在发送一个连续的数据块(我认为C数组是连续的,但我是一名Fortran程序员,并不十分确定),则无需声明新的MPI数据类型,尽管有一些原因可能需要这样做。Scattering用于将数组等分发到多个进程中;您可以使用scatter将数组的每一行发送到不同的进程。因此,对于您的整数连续数组示例,最简单的选项是像这样进行广播(请注意我的糟糕的C技能):
MPI_Bcast(&buf, numRows*numCols, MPI_INT, root, MPI_COMM_WORLD)

where

&buf是数组中第一个元素的地址

numRows*numCols 当然是二维数组中元素的数量

MPI_INT 是(可能)要使用的内置数据类型

root 是广播数组的进程的排名

MPI_COMM_WORLD 是通常的默认通信器,如果需要可以更改

不要忘记广播是一种集体操作,所有进程都会进行相同的调用。

如果您的数组不是连续的,请再次发布一些示例数组大小,我们将找出如何定义MPI数据类型。


0
MPI_Send(tempmat,16,MPI_INT,0,0,MPI_COMM_WORLD);

MPI_Recv(resultmaster,16,MPI_INT,MPI_ANY_SOURCE , 0, MPI_COMM_WORLD, &stat);

当使用上述API时,我只得到矩阵的第一行。


-1

你的二维数组不能直接传递给另一个进程,因为虚拟地址可能不同;也就是说,第一维数组指向其他数组的指针在任何其他进程上都没有意义。因此,您必须分别传递每个数组,并在接收方手动重新组装您的“二维数组”。

2)广播与散射。广播将完整的数组发送到通信器中的所有其他MPI等级。相反,散射将源数组分布到所有其他MPI等级上。也就是说,使用广播,每个等级都会接收源数组的副本,而使用散射,每个等级都会接收数组的不同部分。


你肯定可以直接传递它,只需为该任务定义一个MPI数据类型:请参见我的答案。在接收端,内存地址确实没有意义,但是不应该发送地址,而只发送数据。 - suszterpatt
@suszterpatt:我的意思是直接传递指针数组并期望它能正常工作。你的解决方案本质上就是我提到的手动重新组装,只不过你通过定义MPI数据类型让MPI库来完成重新组装。使用数据类型可能比手动操作更有效率,也可能不是。 - janneb
你仍然不需要单独传递每个数组,可以使用单个消息完成。请记住,与运行普通代码相比,消息传递通常非常耗时,因此您应该尽可能少地发送消息。定义数据类型不仅允许将整个数组作为一个消息发送(如果计算数组大小,则为两个),还可以减轻程序员手动重建数组的负担,从而降低出错的可能性。 - suszterpatt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接