1

我有一个看起来像这样的循环:

do j=1,100
    do i=1,1000 
      combined_array(i,j)=combined_array(i,j-1)
      call foo(combined_array(i,j))
    enddo
enddo

subroutine foo(x)
    x= somefunction(x)
end subroutine foo

我想拆分计算,但列中存在依赖关系。如果它不存在,我可以在列上拆分任务并使用 allgather(按列 划分二维数组并使用 allgather)对于这个循环,我可以在行上拆分任务,但是如何使用 allgather 组合结果?每个等级获得的块在内存中不连续

4

1 回答 1

2

MPI 为此类问题提供了跨步向量类型。您将使用等于总列高的步幅和等于子块中的行数的块大小来构造这种类型。您还可以为这种类型指定一个等于一行中的列数的计数。这是一个具体的示例:假设您有一个REAL包含nr行和nc列的矩阵,并且每个进程都包含一个包含nr1行的子块。然后你会这样做:

integer :: rowtype

call MPI_TYPE_VECTOR(nc, nr1, nr, MPI_REAL, rowtype, ierr)
call MPI_TYPE_COMMIT(rowtype, ierr)

假设您想将一些数据接收到从 row 开始的子块中myrow。使用这种新类型,您可以简单地执行以下操作:

call MPI_RECV(array(myrow,1), 1, rowtype, src, tag, &
              MPI_COMM_WORLD, status, ierr)

它的工作原理是这样的 - 从子块的左上角元素的提供的地址开始(即array(myrow,1)),它将nr1从接收到的消息中放置元素,然后将跳过nr - nr1的元素array,然后放置nr1更多元素并再次跳过nr - nr1元素等等,nc次数.

但是这里有一个问题。rowtype类型的范围是nc*nr元素。您不能这样使用它,MPI_(ALL)GATHERV()因为您只能将每个片段的开头定位在类型范围的倍数的偏移量处,即nc*nr. 为了克服这个限制,MPI 允许您使用MPI_TYPE_CREATE_RESIZED. 它需要一个类型并构建一个具有相同类型映射的新类型(例如,将使用与旧类型相同的“配方”在内存中布置元素),但是在计算偏移量和其他取决于类型范围的事情时, MPI 将使用用户提供的值而不是真实值。您需要做的是将范围更改myrow为等于nr1类型元素的范围MPI_REAL. 它是这样完成的:

integer(kind = MPI_ADDRESS_KIND) :: lb, extent
integer :: rowtype_resized

call MPI_TYPE_GET_EXTENT(MPI_REAL, lb, extent, ierr)
extent = extent * nr1
call MPI_TYPE_CREATE_RESIZED(rowtype, 0, extent, rowtype_resized, ierr)
call MPI_TYPE_COMMIT(rowtype_resized, ierr)

现在您可以使用rowtype_resized来接收nr1行和nc列的子块,但您可以定位它们以便从大的任何行开始,array即多个或nr1不仅在总大小的倍数上array。然后,您可以像这样进行:

call MPI_ALLGATHER(smallarray, nr1*nc, MPI_REAL, &
                   array, 1, rowtype_resized, MPI_COMM_WORLD, ierr)

smallarray这会将小数组(每nr1行和每列)的内容收集nc到大数组array(每nr1 * #processes行和每nc列)中。

您甚至可以更加灵活,将向量类型注册为块长度为 1 而不是nr1. 这将允许您发送单行。然后,您可以创建一个范围为一个MPI_REAL元素的调整大小的类型,并用于MPI_(ALL)GATHERV收集不同大小的子块。

我知道这有点棘手,但随着时间的推移,人们学会了如何掌握 MPI 的类型系统。

于 2012-07-20T20:40:23.140 回答