Ama*_*ama 3 mpi multidimensional-array
我是使用MPI的新手.我的问题如下:我有一个2000行和3列的矩阵存储为2D数组(不是连续的数据).在不改变数组结构的情况下,根据进程数np,每个进程都应该得到矩阵的一部分.示例:A:由2列组成的2000个数组的二维数组,np = 2,然后P0得到A的前半部分,它是前1000行乘3列的2D数组,P1得到后半部分,即第二列1000行数为3列.现在np可以是任何数字(只要它划分行数).有什么简单的方法吗?我将不得不使用FORTRAN 90进行此分配.谢谢
由于列主要存储,使用分散/聚集操作直接在Fortran中按行分布2D阵列是棘手的(但并非不可能).以下是两种可能的解
纯Fortran 90解决方案:使用Fortran 90,您可以指定数组部分A(1:4,2:3),这样可以从矩阵中取出一个小的4x2块A.您可以将数组切片传递给MPI例程.注意当前的MPI实现(符合现在的旧MPI-2.2标准),编译器将创建段数据的临时连续副本并将其传递给MPI例程(因为临时存储的生命周期没有很好地定义,一个不应该将数组sectons传递给非阻塞MPI操作,例如MPI_ISEND).MPI-3.0引入了新的非常现代的Fortran 2008接口,允许MPI例程直接接收数组部分(没有中间数组),并支持将部分传递给非阻塞调用.
对于数组部分,您只需DO在根进程中实现一个简单的循环:
INTEGER :: i, rows_per_proc
rows_per_proc = 2000/nproc
IF (rank == root) THEN
DO i = 0, nproc-1
IF (i /= root) THEN
start_row = 1 + i*rows_per_proc
end_row = (i+1)*rows_per_proc
CALL MPI_SEND(mat(start_row:end_row,:), 3*rows_per_proc, MPI_REAL, &
i, 0, MPI_COMM_WORLD, ierr)
END IF
END DO
ELSE
CALL MPI_RECV(submat(1,1), 3*rows_per_proc, MPI_REAL, ...)
END IF
Run Code Online (Sandbox Code Playgroud)
纯MPI解决方案(也适用于FORTRAN 77):首先,您必须声明一个矢量数据类型MPI_TYPE_VECTOR.块的数量是3,块长度将是每个进程应该获得的行数(例如1000),步幅应该等于矩阵的总高度(例如2000).如果调用此数据类型blktype,则以下内容将发送矩阵的上半部分:
REAL, DIMENSION(2000,3) :: mat
CALL MPI_SEND(mat(1,1), 1, blktype, p0, ...)
CALL MPI_SEND(mat(1001,1), 1, blktype, p1, ...)
Run Code Online (Sandbox Code Playgroud)
调用MPI_SEND与blktype将采取1000从指定的起始地址的元素,然后跳到下一个2000 - 1000 = 1000元素,再来1000等,3时间在总.这将形成大矩阵的1000行子矩阵.
您现在可以运行循环以向通信器中的每个进程发送不同的子块,从而有效地执行分散操作.为了接收这个子块,接收过程可以简单地指定:
REAL, DIMENSION(1000,3) :: submat
CALL MPI_RECV(submat(1,1), 3*1000, MPI_REAL, root, ...)
Run Code Online (Sandbox Code Playgroud)
如果您不熟悉MPI,那么您需要了解Fortran中按行散布矩阵的所有内容.如果你很清楚MPI的类型系统是如何工作的,那么请提前阅读以获得更优雅的解决方案.
(有关如何使用Jonathan Dursi的详细说明,请参见此处MPI_SCATTERV.他的解决方案涉及在列中拆分C矩阵,这基本上会产生与此处相同的问题,因为C以行主要方式存储矩阵.Fortran版本如下.)
你也可以利用,MPI_SCATTERV但它涉及很多.它建立在上面提出的纯MPI解决方案之上.首先,您必须将blktype数据类型的大小调整为一个新类型,该类型的范围等于MPI_REAL可以指定数组元素中的偏移量的范围.这是必需的,因为偏移量MPI_SCATTERV是以指定的数据类型范围的倍数指定的,而范围blktype是矩阵本身的大小.但是由于跨步存储,两个子块仅以4000相隔的字节开始(1000以典型程度为单位MPI_REAL).要修改类型的范围,可以使用MPI_TYPE_CREATE_RESIZED:
INTEGER(KIND=MPI_ADDRESS_KIND) :: lb, extent
! Get the extent of MPI_REAL
CALL MPI_TYPE_GET_EXTENT(MPI_REAL, lb, extent, ierr)
! Bestow the same extent upon the brother of blktype
CALL MPI_TYPE_CREATE_RESIZED(blktype, lb, extent, blk1b, ierr)
Run Code Online (Sandbox Code Playgroud)
这会创建一个新的数据类型,blk1b它具有所有特性blktype,例如可用于发送整个子块,但是当在数组操作中使用时,MPI只会使数据指针的大小增加MPI_REAL而不是大小为整个矩阵.使用这种新类型,您现在可以将每个块的开头定位MPI_SCATTERV在任何元素上mat,包括任何矩阵行的开头.有两个子块的示例:
INTEGER, DIMENSION(2) :: sendcounts, displs
! First sub-block
sendcounts(1) = 1
displs(1) = 0
! Second sub-block
sendcounts(2) = 1
displs(2) = 1000
CALL MPI_SCATTERV(mat(1,1), sendcounts, displs, blk1b, &
submat(1,1), 3*1000, MPI_REAL, &
root, MPI_COMM_WORLD, ierr)
Run Code Online (Sandbox Code Playgroud)
这里第一个子块的位移0与矩阵的开始一致.第二子块的位移1000,即它将在第一列的第1000行开始.在接收方的侧面,数据计数参数是3*1000元素,它与子块类型的大小相匹配.