使用MPI_Scatter发送矩阵的列

我正在尝试使用MPI编写矩阵向量乘法程序。 我正在尝试将矩阵的列发送到单独的进程并在本地计算结果。 最后,我使用MPI_SUM操作执行MPI_SUM

发送矩阵的行很容易,因为C按行主顺序存储数组,但列不是(如果不逐个发送)。 我在这里读到了这个问题:

MPI_Scatter – 发送2D数组的列

Jonathan Dursi建议使用新的MPI数据类型,这就是我根据自己的需要调整代码所做的工作:

  double matrix[10][10]; double mytype[10][10]; int part_size; // stores how many cols a process needs to work on MPI_Datatype col, coltype; // ... MPI_Type_vector(N, 1, N, MPI_DOUBLE, &col); MPI_Type_commit(&col); MPI_Type_create_resized(col, 0, 1*sizeof(double), &coltype); MPI_Type_commit(&coltype); // ... MPI_Scatter(matrix, part_size, coltype, mypart, part_size, coltype, 0, MPI_COMM_WORLD); // calculations... MPI_Reduce(local_result, global_result, N, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); 

这很好用,但我不能说我真的明白它是如何工作的。

  1. MPI_Type_vectorMPI_Type_vector存储在内存中的?
  2. MPI_Type_create_resized()如何工作以及它到底做了什么?

请记住,我是MPI的初学者。 提前致谢。

在我对这个问题的 回答中有一个很长的描述这个问题 :许多人有这些问题的事实certificate它并不明显,这些想法需要一些时间来适应。

重要的是要知道MPI数据类型描述的内存布局。 MPI_Type_vector的调用序列是:

 int MPI_Type_vector(int count, int blocklength, int stride, MPI_Datatype old_type, MPI_Datatype *newtype_p) 

它创建了一个新类型,它描述了每个stride项的内存布局,其中有一块块blocklength项被拉出,以及这些块的总计count 。 这里的项目以old_type为单位。 所以,例如,如果你打电话(在这里命名参数,你实际上不能在C中做,但:)

  MPI_Type_vector(count=3, blocklength=2, stride=5, old_type=MPI_INT, &newtype); 

然后newtype将在内存中描述一个布局,如下所示:

  |<----->| block length +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | X | X | | | | X | X | | | | X | X | | | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |<---- stride ----->| count = 3 

其中每个方块是一个整数大小的内存块,大概是4个字节。 请注意,步幅是从一个块的开始到下一个块的开始的整数距离,而不是块之间的距离。

好的,所以在你的情况下你打电话

  MPI_Type_vector(N, 1, N, MPI_DOUBLE, &col); 

这将取count = N个块,每个块的大小块长度blocklength=1 MPI_DOUBLE s,每个stride=N块的起点之间有一个空格stride=N MPI_DOUBLE s。 换句话说,它将每N次加倍,总共N次; 非常适合从(连续存储的)NxN双精度数组中提取一列。 一个方便的检查是查看正在count*stride = N*N多少数据( count*stride = N*N ,这是矩阵的完整大小,检查)以及实际包含多少数据( count*blocksize = N ,这是大小列,检查。)

如果您只需要调用MPI_Send和MPI_Recv来交换各个列,那么您就完成了; 你可以使用这种类型来描述列的布局,你会没事的。 但还有一件事。

你想调用MPI_Scatter ,它将第一个coltype(比如说)发送到处理器0,下一个coltype发送到处理器1,等等。如果你用一个简单的1d数组做,那么很容易找出“下一个”数据的位置类型是; 如果你将1 int分散到每个处理器,那么“next”int会在第一个int结束后立即开始。

但是你的新coltype列的总范围从列的开头到N*N MPI_DOUBLE后来 – 如果MPI_Scatter遵循相同的逻辑(它确实),它将开始寻找外面的“下一个”列。矩阵记忆完全,等等与下一个和下一个。 你不仅没有得到你想要的答案,程序可能会崩溃。

解决这个问题的方法是告诉MPI,为了计算“下一个”所在位置,这个数据类型的“大小”是一列开始和下一列开始之间的内存大小; 也就是说,只有一个MPI_DOUBLE 。 这不会影响发送的数据量,这仍然是1列数据; 它只会影响“下一行”计算。 对于数组中的列(或行),您只需将此大小发送到内存中的相应步长,MPI将选择要发送的正确的下一列。 如果没有此resize运算符,您的程序可能会崩溃。

当您有更复杂的数据布局时,例如在上面链接的2d数组示例的2d块中,那么“next”项之间没有一个步长; 你仍然需要做大小调整技巧,使大小成为一些有用的单位,但是你需要使用MPI_Scatterv而不是分散来明确指定要发送的位置。