2016-05-30 43 views
1

假設程序運行於xpypzp進程。 使用笛卡兒傳播者,使得這些過程可以被認爲是安排在維度網格(xp,yp,zp)中。 在這個程序中,根進程(0)聲明並分配一個3D數組Atot,它將由每個進程(包含根)聲明的3D數組A填充。具有不同大小數據類型的MPI通信

INTEGER, DIMENSION(3) :: Ntot 
INTEGER, DIMENSION(3) :: N 
INTEGER, DIMENSION(:,:,:), ALLOCATABLE :: Atot 
INTEGER, DIMENSION(:,:,:), ALLOCATABLE :: A 
: 
! the 3 elements of the array N are determined by dividing the corresponding 
! element of the array Ntot by the number of process in that direction 
! taking into account the reminder of the division. 
: 
IF (myid == 0) THEN ! myid is the process' rank 
    ALLOCATE(Atot(Ntot(1),Ntot(2),Ntot(3)) 
END IF 
ALLOCATE(A(N(1),N(2),N(3)) 
A = myid 

哪一種是最正確,最簡單,最有效的溝通方式? 我正在考慮MPI_gather:每個進程會發送由N(1)*N(2)*N(3)MPI_INTEGER s組成的整個數組A,並且根進程應該接收它們,然後將它們接收到對應於一個多維數據集的單個MPI派生數據類型中(MPI_type_vector應該遞歸地使用兩次, 我對嗎?)。 可以這樣做嗎?

即使此方法有效,它的聲音容易我,當沿笛卡爾通信的每個方向上的進程數整除的Ntot相應元素,也就是,當陣列A在每個過程中的相同的尺寸。這是Ntot = (/9,9,9/)的情況。

Ntot = (/10,10,10/)怎麼辦? mpi派生的數據類型在不同的進程中會有不同的維度,那麼是否仍然可以使用MPI_ghather?

編輯

我不排除MPI_GATHERV可能是解決方案的一部分。但是,它允許每個進程發送(和根進程接收)不同數量的數據,即不同數量的MPI_INTEGERS(在簡單示例中)。但是,在我處理的情況下,根進程必須接收三維數組Atot中的數據。爲此,我認爲定義MPI派生數據類型可能很有用,我們將其命名爲smallcube。在這種情況下,每個進程發送整個數組A,而主進程將從每個進程接收1個數據類型smallcube。重點是small cube沿着三個維度具有不同的長度,具體取決於它在笛卡爾網格中的位置(假設長度沒有被沿着三維的處理數量均勻分開)。

+1

正確的如果我錯了,但我認爲MPI_GATHERV(注意V)允許來自每個進程的不同數量的數據,也許這是你在你的問題的最後部分尋找什麼? – Coriolis

+0

我編輯了這個問題:) –

+2

這可以通過使用'MPI_ALLTOALLW'來模擬不存在的'MPI_SCATTERW'來實現。還有另一種方法,用Jonathan Dursi [這裏](http://stackoverflow.com/a/29476914/1374437)對C進行規範回答(還包括'MPI_ALLTOALLW'方法)。希望你能理解它是如何工作的,並將它翻譯成Fortran(它應該相對簡單)。我可以做到這一點,如果沒有其他人會這樣做,那麼我有更多的空閒時間。 –

回答

3

正如評論中提到的,如果你真的想把所有的數據提取到一個處理器上,那麼MPI_Type_create_subarray可能是一個很好的方法。鑑於我剛剛在我自己的項目中使用了MPI_Type_create_subarray,我以爲我會嘗試提供一個有效的示例答案(請注意,我錯過了錯誤檢查和我正在聲明的類型)。

program subarrayTest 
    use mpi 
    implicit none 
    integer, parameter :: n1 = 10, n2=20, n3=32 
    INTEGER, DIMENSION(3) :: Ntot, N, sizes, subsizes, starts 
    INTEGER, DIMENSION(:,:,:), ALLOCATABLE :: Atot, A 
    integer :: iproc, nproc, sendSubType, ierr 
    integer :: nl1, nl2, nl3 !Local block sizes 
    integer :: l1, l2, l3, u1, u2, u3 !Local upper/lower bounds 
    integer :: ip, sendRequest 
    integer, dimension(:), allocatable :: recvSubTypes, recvRequests 
    integer, dimension(:,:,:), allocatable :: boundsArr 

    !MPI Setup 
    call mpi_init(ierr) 
    call mpi_comm_size(mpi_comm_world, nproc, ierr) 
    call mpi_comm_rank(mpi_comm_world, iproc, ierr) 

    !Set grid sizes 
    Ntot = [n1,n2,n3] 
    !For simplicity I'm assuming we only split the last dimension (and it has nproc as a factor) 
    !although as long as you can specify l* and u* this should work (and hence nl* = 1+u*-l*) 
    if(mod(n3,nproc).ne.0) then 
    print*,"Error: n3 must have nproc as a factor." 
    call mpi_abort(mpi_comm_world,MPI_ERR_UNKNOWN,ierr) 
    endif 
    nl1 = n1 ; l1 = 1 ; u1=l1+nl1-1 
    nl2 = n2 ; l2 = 1 ; u2=l2+nl2-1 
    nl3 = n3/nproc ; l3 = 1+iproc*nl3 ; u3=l3+nl3-1 
    N = [nl1,nl2,nl3] 

    !Very lazy way to ensure proc 0 knows the upper and lower bounds for all procs 
    allocate(boundsArr(2,3,0:nproc-1)) 
    boundsArr=0 
    boundsArr(:,1,iproc) = [l1, u1] 
    boundsArr(:,2,iproc) = [l2, u2] 
    boundsArr(:,3,iproc) = [l3, u3] 
    call mpi_allreduce(MPI_IN_PLACE,boundsArr,size(boundsArr),MPI_INTEGER, & 
     MPI_SUM, mpi_comm_world, ierr) 

    !Allocate and populate local data portion 
    IF (iproc == 0) THEN ! iproc is the process' rank 
    ALLOCATE(Atot(Ntot(1),Ntot(2),Ntot(3))) 
    Atot=-1 !So you can check all elements are set 
    END IF 
    ALLOCATE(A(N(1),N(2),N(3))) 
    A = iproc 

    !Now lets create the sub array types 
    !First do the send type 
    sizes=N !The size of the local array 
    subsizes=1+[u1,u2,u3]-[l1,l2,l3] !The amount of data in each dimension to send -- here it's the full local data array but in general it could be a small subset 

    starts = [0,0,0] !These are the lower bounds in each dimension where the sub array starts -- Note MPI assumes 0 indexing here. 
    call mpi_type_create_subarray(size(sizes),sizes, subsizes, starts, & 
     MPI_ORDER_FORTRAN, MPI_INTEGER, sendSubType, ierr) 
    call mpi_type_commit(sendSubType, ierr) 

    !Now on proc0 setup each receive type 
    if (iproc == 0) then 
    allocate(recvSubTypes(0:nproc-1)) !Use 0 indexing for ease 
    sizes = Ntot !Size of dest array 
    do ip=0,nproc-1 
     subsizes=1+boundsArr(2,:,ip)-boundsArr(1,:,ip) !Size of A being sent from proc ip 
     starts = boundsArr(1,:,ip) -1 
     call mpi_type_create_subarray(size(sizes),sizes, subsizes, starts, & 
      MPI_ORDER_FORTRAN, MPI_INTEGER, recvSubTypes(ip), ierr) 
     call mpi_type_commit(recvSubTypes(ip), ierr) 
    end do 
    end if 

    !Now lets use non-blocking communications to transfer data 
    !First post receives -- tag with source proc id 
    if (iproc == 0) then 
    allocate(recvRequests(0:nproc-1)) 
    do ip=0,nproc-1 
     call mpi_irecv(Atot,1,recvSubTypes(ip),ip,ip,& 
      mpi_comm_world,recvRequests(ip),ierr) 
    end do 
    end if 

    !Now post sends 
    call mpi_isend(A,1,sendSubType,0,iproc,mpi_comm_world,& 
     sendRequest, ierr) 

    !Now wait on receives/sends 
    if(iproc == 0) call mpi_waitall(size(recvRequests),recvRequests,& 
     MPI_STATUSES_IGNORE,ierr) 
    call mpi_wait(sendRequest, MPI_STATUS_IGNORE, ierr) 

    if(iproc == 0) print*,Atot 
    call mpi_barrier(mpi_comm_world, ierr) 

    !Now free resources -- not shown 
    call mpi_finalize(ierr) 
end program subarrayTest 

你應該可以編譯這個mpif90。你需要充分利用這一點,以便爲你的情況設置適當的局部邊界,但希望這會提供一個有用的起點。這並不假定任何關於本地數組大小在處理器上是相同的,只要下限和上限(l*u*)設置正確,那麼這應該工作正常。注意我上面的代碼可能並不遵循最佳實踐。

相關問題