我有一个看起来像这样的循环:
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( partition a 2D array column-wise and use allgather) 对于这个循环,我可以在行上拆分任务但是如何使用allgather组合结果?每个等级获得的块在内存中不连续
答案 0 :(得分:1)
MPI为这类问题提供了跨步矢量类型。您可以使用等于总列高度的步幅和等于子块中行数的块大小来构造此类型。您还可以为此类型指定一个等于行中列数的计数。下面是一个具体示例:假设您有一个REAL
矩阵,其中nr
行和nc
列,每个进程都包含一个nr1
行的子块。然后你会这样做:
integer :: rowtype
call MPI_TYPE_VECTOR(nc, nr1, nr, MPI_REAL, rowtype, ierr)
call MPI_TYPE_COMMIT(rowtype, ierr)
假设您希望将某些数据接收到从myrow
行开始的子块中。使用这种新类型,你可以做到:
call MPI_RECV(array(myrow,1), 1, rowtype, src, tag, &
MPI_COMM_WORLD, status, ierr)
它的工作原理是这样 - 从子块的顶部lef元素的提供地址开始(即array(myrow,1)
),它将从接收的消息中放入nr1
个元素,然后将跳过{{1 } {} {}}元素,然后添加nr - nr1
个元素,再次跳过array
个元素,依此类推,nr1
次。
但这里有一个问题。 nr - nr1
类型的范围是nc
个元素。你不能像rowtype
一样使用它,因为你只能将每个部分的开头定位在类型范围的倍数的偏移处,即nc*nr
的倍数。为了克服此限制,MPI允许您使用MPI_(ALL)GATHERV()
人为地更改类型的范围。它需要一个类型并构建一个具有相同类型映射的新类型(例如,将使用与旧类型相同的“配方”在内存中布置元素),但是在计算偏移量和其他依赖于类型范围的事物时,MPI将使用用户提供的值而不是真实值。您需要做的是将nc*nr
的范围更改为等于MPI_TYPE_CREATE_RESIZED
类型的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
列)。
您甚至可以更灵活,并注册块长度为1而不是nr1 * #processes
的矢量类型。这将允许您发送单行。然后,您可以创建一个具有一个nc
元素范围的已调整大小的类型,并使用nr1
来收集不同大小的子块。
我知道这有点棘手,但是及时学会了如何掌握MPI的类型系统。