2015-11-04 66 views
1

對MPI有一定的經驗,但是對於衍生類型等更高級的方面,這與我的問題有關。如何使用MPI發送正確數量的派生類型對象?

我正在使用的代碼有幾個數組,尺寸爲(-1:nx+2,-1:ny+2,-1:nz+2)。爲了清楚起見,每個過程都有自己的值nx,nynz。數組之間有重疊。例如,一個proc上的x(:,:,-1:2)將代表與「僅位於其下」的proc上的x(:,:,nz-1:nz+2)相同的信息。

派生cell_zface類型被定義爲:

idir = 3 
sizes = (/nx_glb, ny_glb, nz_glb/) !These nums are the same for all procs. 
subsizes = (/nx, ny, 2/) 
mpitype = MPI_DATATYPE_NULL 
CALL MPI_TYPE_CREATE_SUBARRAY(3, sizes, subsizes, starts, & 
    MPI_ORDER_FORTRAN, mpireal, mpitype, errcode) 
CALL MPI_TYPE_COMMIT(mpitype, errcode) 
cell_zface = mpitype 

現在,這種派生類型被使用,成功地在幾個MPI_SENDRECV電話。例如

CALL MPI_SENDRECV(& 
     x(-1,-1, 1), 1, cell_zface, proc_z_min, tag, & 
     x(-1,-1,nz+1), 1, cell_zface, proc_z_max, tag, & 
     comm, status, errcode) 

據我所知,這個呼叫被髮送和接收兩個「水平」切片特效之間的陣列(即X-Y切片)。

我想做一點不同的事情,即發送四個「水平」切片。所以我嘗試

call mpi_send(x(-1,-1,nz-1), 2, cell_zface, & 
        proc_z_max, rank, comm, mpierr) 

隨附的接收。

最後,我的問題:代碼運行,但錯誤。 AFAICT,這隻發送兩個水平分片,即使我使用「2」而不是「1」作爲計數參數。我可以做兩個調用mpi_send解決這個問題:

call mpi_send(x(-1,-1,nz-1), 1, cell_zface, & 
        proc_z_max, rank, comm, mpierr) 
call mpi_send(x(-1,-1,nz+1), 1, cell_zface, & 
        proc_z_max, rank, comm, mpierr) 

伴隨接收,但是這肯定是不漂亮。

那麼,爲什麼mpi_send只發送兩個水平切片,即使我將count參數設置爲「2」?有沒有一種乾淨的方式來做我想在這裏做的事情?

+0

你必須明白,即使你有自己的「類型」,你真正擁有的東西都是一個巨大的連續的記憶塊。因此,雖然可以發送兩個'cell_zface'類型,但類型本身在內存中的大小與您類型使用的第一個和最後一個(在1D)內存位置之間的距離一樣大。也就是說,你的類型的大小並不是真正的'nx * ny * nz'。 – NoseKnowsAll

+0

要完成您正在尋找的任務,您必須在不同的範圍內給出您的派生數據類型。請注意,數據類型的大小將是相同的,但「範圍」(或此數據類型的內存連續版本數量將包含)將不會。我相信您可以通過[MPI_Type_create_resized](http://www.mpich.org/static/docs/v3.1/www3/MPI_Type_create_resized.html)完成此操作。 – NoseKnowsAll

回答

1

每個MPI數據類型都有兩種大小,可以這麼說。一種是真正的大小,即存儲數據類型引用的所有重要數據所需的內存量。可以把它看作實際消息中該數據類型的元素所佔用的空間量。

另一個大小就是所謂的程度。在MPI每個數據類型是類型的指令的集合:「去從所提供的緩衝器位置偏移DISP 和讀/寫類型基本類型的元素」。全部集合(類型,顯示對被稱爲數據類型的類型映射。最小偏移量稱爲下限,並且在該偏移量+任何所需填充處的基本類型的最大偏移量+大小被稱爲上限。數據類型的範圍是上限和下限之間的差異,並給出了最短連續內存區域的大小,其中包括數據類型訪問的所有位置。

由於MPI要求在任何通信操作中沒有多次讀取或寫入存儲器位置,因此typemap中的對必須引用不相交的位置。因此,數據類型的真實範圍總是大於或等於其大小。

當訪問該數據類型的連續元素時,MPI使用數據類型的範圍。以下聲明:

MPI_SEND(buf, n, dtype, ...) 

結果:

  • MPI需要dtype類型的一個元件從位置buf以下編碼爲dtype的typemape規則;
  • MPI採用從位置buf + extent(dtype)開始的下一個元素;
  • ...
  • MPI以位置buf + (n-1)*extent(dtype)開始的第n個元素。

原始數據類型,如MPI_INTEGERMPI_REAL等有其程度相匹配的基本類型(INTEGERREAL等)的大小+由架構授權的任何填充,這使得它可以發送陣列基本類型只需指定元素的數量即可。

現在,回到你的案例。您正在創建一個涵蓋nx_glb x ny_glb x nz_glb數組的nx x ny x 2子數組的數據類型。該數據類型的大小確實是nx * ny * 2倍,大小爲mpireal,但範圍實際上是乘以mpireal的範圍。換句話說:

MPI_SEND(buf, 2, cell_zface, ...) 

不會在buf提取從大陣列兩個連續nx x ny x 2磚。相反,它會提取從每個大小nx_glb x ny_glb x nz_glb的兩個連續的陣列,從位置開始之一板坯(開始 X,啓動ÿ,啓動Ž每個陣列英寸如果您的程序在運行時沒有出現段錯誤,請考慮自己的幸運。

現在出現棘手的部分。 MPI允許爲每個數據類型提供一個假範圍(這就是爲什麼我通過人爲地設置下限和上限的值來調用前面定義的範圍「真」的原因)。這樣做不會影響數據類型或其類型映射的大小(即,MPI仍會進入相同的偏移量並操作相同基本類型的元素),但會影響MPI在訪問給定數據類型的連續元素時在內存中的步幅。此前,通過將數據類型「夾在」特殊假型MPI_LBMPI_UB的元素之間的狹窄中來設置範圍。自從MPI-2以來,使用MPI_TYPE_CREATE_RESIZED來實現相同。

integer(kind=MPI_ADDRESS_KIND) :: lb, extent 
integer :: newtype 

! First obtain the extent of the old type used to construct cell_zface 
call MPI_TYPE_GET_EXTENT(mpireal, lb, extent, errcode) 
! Adjust the extent of cell_zface 
extent = (nx_glb * ny_glb * subsizes(3)) * extent 
call MPI_TYPE_CREATE_RESIZED(cell_zface, lb, extent, newtype, errcode) 
call MPI_TYPE_COMMIT(newtype, errcode) 
! Get rid of the previous type 
call MPI_TYPE_FREE(cell_zface, errcode) 
cell_zface = newtype 

您現在可以使用cell_zface發送幾個連續的板。

另一種大概更簡單的方法是設置數組等於子陣列的3-RD維度的大小的3-RD尺寸的大小,同時主叫MPI_TYPE_CREATE_SUBARRAY

idir = 3 
subsizes = (/nx, ny, 2/) 
sizes = (/nx_glb, ny_glb, subsizes(3)/) !These nums are the same for all procs. 
mpitype = MPI_DATATYPE_NULL 
CALL MPI_TYPE_CREATE_SUBARRAY(3, sizes, subsizes, starts, & 
    MPI_ORDER_FORTRAN, mpireal, mpitype, errcode) 
CALL MPI_TYPE_COMMIT(mpitype, errcode) 
cell_zface = mpitype 

在兩種情況下我假設starts(3)等於0

+0

沒想到做了太多的工作來答案!非常感謝您提供完整且非常有用的答案。你是男人! –

+0

因此,如果我理解正確,這種數據類型的跨度幾乎與整個3D數組的大小相當。這裏有一個問題,我知道你不能明確地回答,但會感激你的任何洞察力:你可能已經發現我沒有寫這個代碼。那麼,它有一個與整個數組大小相等的步幅是否有意義?我想不出任何有用的應用程序。有一個步幅等於一個維度,或兩個維度的產品,當然。但整個陣列呢?謝謝。 –

+1

子數組數據類型構造函數的主要意圖用於MPI-IO。不同的MPI等級提供相同的數組維度並指定其中的不同部分,從而使他們(並行)訪問以單個二進制文件形式存儲的數組數據。這種數據類型的範圍有意地等於整個數組的大小 - 它允許從連續存儲的這樣的數組集合中容易地讀取。子數組構造函數也可以用於表示域分解中的暈圈區域的數據類型,但必須小心並牢記其起源。 –