2014-06-18 91 views
6

我正在學習有關Fortran中的BCASTing數據類型,並有一個代碼,它從終端獲取兩個值並在每個進程上顯示它們。對於integer/integer和integer/real類型的組合value1/value2,這適用,但對於整數/實數* 8組合,則失敗。派生的數據類型與MPI

的代碼是:

use mpi 
implicit none 

integer :: ierror, pid, ncpu, root = 0 

integer :: counts, newtype, extent 
integer, dimension(2) :: oldtypes, blockcounts, offsets 

type value 
    integer :: value1 = 0 
    real*8 :: value2 
end type 

type (value) input 

call MPI_INIT(ierror) 
call MPI_COMM_RANK(MPI_COMM_WORLD, pid, ierror) 
call MPI_COMM_SIZE(MPI_COMM_WORLD, ncpu, ierror) 

! setup of 1 MPI_INTEGER field: value1 
offsets(1) = 0 
oldtypes(1) = MPI_INTEGER 
blockcounts(1) = 1 

! setup of 1 MPI_REAL8 field: value2 
call MPI_TYPE_EXTENT(MPI_INTEGER, extent, ierror) !determine offset of MPI_INTEGER 
offsets(2) = blockcounts(1)*extent     !offset is 1 MPI_INTEGER extents 
oldtypes(2) = MPI_REAL8 
blockcounts(2) = 1 

! define struct type and commit 
counts = 2 !for MPI_INTEGER + MPI_REAL8 
call MPI_TYPE_STRUCT(counts, blockcounts, offsets, & 
        oldtypes, newtype, ierror) 
call MPI_TYPE_COMMIT(newtype, ierror) 

do while (input%value1 >= 0) 
    if (pid == root) then 
     read(*,*) input 
     write(*,*) 'input was: ', input 
    end if 
    call MPI_BCAST(input, 1, newtype, & 
        root, MPI_COMM_WORLD, ierror) 
    write(*,*), 'process ', pid, 'received: ', input 
end do 

call MPI_TYPE_FREE(newtype, ierror) 
call MPI_FINALIZE(ierror) 

它可以檢查通過改變相應的聲明和OLDTYPE該整數/整數和整數/實做工精細。整數/實數* 8組合失敗,例如,輸入-1 2.0生成:

input was:   -1 2.0000000000000000  
process   0 received:   -1 2.0000000000000000  
process   1 received:   -1 0.0000000000000000  
process   2 received:   -1 0.0000000000000000  
process   3 received:   -1 0.0000000000000000 

This線程有類似的問題,建議使用MPI_TYPE_EXTENT是不正確的,因爲可能有未考慮額外的填充。不幸的是,我還沒有能夠解決這個問題,希望這裏有人能夠啓發我。

提前THX

回答

7

你有基本的想法正確的 - 你所創建的結構,但你假定雙精度值立即被存儲後的整數值,並且通常是不正確的。 Hristo的回答說,你的鏈接在C中給出了很好的答案。

問題是,編譯器通常會爲你的數據結構字段​​。大多數系統可以讀取/寫入在內存中對齊的值比執行非對齊訪問的速度快得多,如果它們完全可以執行的話。通常情況下,要求是按照元素大小進行對齊;即一個8字節的雙精度數字必須與8字節的邊界對齊(也就是說,它的第一個字節的地址是零模8),而整數只需要4字節對齊。這幾乎肯定意味着在整數和雙精度之間有4個字節的填充。

在很多情況下,您可以哄騙編譯器放鬆這種行爲 - 在fortran中,您還可以使用sequence關鍵字來要求連續存儲數據。無論哪種方式,從性能的角度來看(這就是爲什麼您使用Fortran和MPI,一個假設),這幾乎從來都不是正確的做法,但它可以用於與其他外部強加的字節到字節的兼容性數據類型或格式。

考慮到可能由於性能原因而產生的填充效果,可以將對齊和硬編碼假設爲您的程序;但是這可能不是正確的做法;如果添加其他字段,或將實數的類型更改爲4個字節的單精度數字等,則代碼將再次出錯。最好是用MPI_Get_address明確查找位置和計算正確的偏移自己:

integer(kind=MPI_Address_kind) :: startloc, endloc  
integer :: counts, newtype 
integer, dimension(2) :: oldtypes, blockcounts, offsets 

type value 
    integer :: value1 = 0 
    double precision :: value2 
end type 

type (value) :: input 

!...  

! setup of 1 MPI_INTEGER field: value1 
call MPI_Get_address(input, startloc, ierror) 
oldtypes(1) = MPI_INTEGER 
blockcounts(1) = 1 
call MPI_Get_address(input%value1, endloc, ierror) 
offsets(1) = endloc - startloc 

oldtypes(2) = MPI_DOUBLE_PRECISION 
blockcounts(2) = 1 
call MPI_Get_address(input%value2, endloc, ierror) 
offsets(2) = endloc - startloc 

if (pid == 0) then 
    print *,'offsets are: ', offsets 
endif 

需要注意的是,如果你有這樣的派生類型的數組,覆蓋填充的一個項目的最後一個元素之間的情況下,在下一個的開始,你也想要明確地測量它,並且用MPI_Type_create_resized來設置類型的整體大小 - 該類型的一個成員的開始和下一個的開始之間的偏移量。

+0

+1很好的答案和例子。 – casey

+0

+1謝謝澄清。我已經在玩MPI_GET_ADDRESS,但沒有得到它的工作。你的例子非常漂亮! – nluigi