2012-11-29 45 views
3

我對使用MPI相當陌生。我的問題如下:我有一個2000行和3列存儲爲二維數組的矩陣(不是連續的數據)。在不改變數組結構的情況下,根據進程數np,每個進程應該得到矩陣的一部分。 示例: 答:2000列數組的二維數組3列,np = 2,然後P0得到A的前半部分,它將是前1000行3列的二維數組,P1得到後半部分第二個1000行乘3列。 現在NP可以是任何數字(只要它劃分行數)。任何簡單的方法來解決這個問題? 我將不得不使用FORTRAN 90來完成這項任務。 感謝您的Fortran二維數組的分解矩陣以便每個進程都可以使用MPI獲取其矩陣的份額

回答

7

逐行分佈是使用分散棘手的(但不是不可能)/聚集的直接,因爲列主存儲操作。有兩種可能的解決方案

純粹的Fortran 90解決方案:使用Fortran 90,您可以指定像A(1:4,2:3)這樣的陣列部分,這會從矩陣A中取出一個小的4x2塊。您可以將數組切片傳遞給MPI例程。請注意,當前的MPI實現(符合現在的舊MPI-2.2標準)時,編譯器會創建部分數據的臨時連續副本,並將其傳遞給MPI例程(因爲臨時存儲器的生命週期尚未定義好, 不應該傳遞數組部分到非阻塞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 

純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, ...) 

調用MPI_SENDblktype將採取從指定的起始地址1000元素,然後跳到下一個2000 - 1000 = 1000元素,採取另一種1000依次類推,3次。這將形成大矩陣的1000行子矩陣。

您現在可以運行一個循環來爲通信器中的每個進程發送一個不同的子塊,從而有效地執行分散操作。爲了獲得這個子塊,在接收過程可以簡單地註明:

REAL, DIMENSION(1000,3) :: submat 

CALL MPI_RECV(submat(1,1), 3*1000, MPI_REAL, root, ...) 

如果你是新的MPI,這是所有你需要知道有關的Fortran由行散射矩陣。如果您很清楚MPI的類型系統是如何工作的,那麼請繼續閱讀以獲得更優雅的解決方案。


(見here關於如何通過喬納森·德西這樣做與MPI_SCATTERV一個很好的說明。他的解決方案涉及按列分割C矩陣,這在本質上提出了和C一樣的問題,因爲C以行主要方式存儲矩陣。 Fortran版本如下。)

您也可以利用MPI_SCATTERV,但它是相當複雜的。它建立在上述純MPI解決方案的基礎上。首先,您必須將blktype數據類型調整爲新類型,其範圍等於MPI_REAL,以便可以指定數組元素中的偏移量。這是必要的,因爲MPI_SCATTERV中的偏移量是以指定的數據類型的範圍的倍數指定的,並且blktype的範圍是矩陣本身的大小。但是由於存儲的跨越,兩個子塊將僅以4000字節開始(1000MPI_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) 

這將創建一個新數據類型,blk1b,其具有blktype所有特性,例如可用於發送整個子塊,但在陣列操作中使用時,MPI只會將數據指針的大小改爲單個MPI_REAL而不是整個矩陣的大小。使用這種新類型,您現在可以在mat的任何元素上定位每個塊的開始位置MPI_SCATTERV,包括任何矩陣行的開始。例如具有兩個子塊:

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) 

這裏所述第一子塊的位移是0,其與基質的開始一致。第二子塊的位移是1000,即它將從第一列的第1000行開始。在接收端,數據計數參數是3*1000元素,它與子塊類型的大小相匹配。

+0

你指的是這個嗎? http://stackoverflow.com/questions/10788180/sending-columns-of-a-matrix-using-mpi-scatter – dreamcrash

+0

@dreamcrash - 絕對!我會鏈接到它。 –

+0

@lleiv:哇,很好的回答!非常感謝! –