2014-06-18 59 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