按行划分2D数组并使用allgather?

时间:2012-07-20 19:49:08

标签: parallel-processing fortran mpi fortran90 openmpi

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

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组合结果?每个等级获得的块在内存中不连续

1 个答案:

答案 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的类型系统。