使用MPI派生数据类型

nlu*_*igi 8 fortran types mpi

我正在学习Fortran中的BCASTing数据类型,并且有一个代码从终端获取两个值并在每个进程上显示它们.对于整数/整数和整数/实数类型的组合value1/value2,这是有效的,但是对于组合整数/实数*8,它会失败.

代码是:

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)
Run Code Online (Sandbox Code Playgroud)

通过更改相应的声明和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
Run Code Online (Sandbox Code Playgroud)

具有类似问题的线程表明使用MPI_TYPE_EXTENT不正确,因为可能存在未考虑的额外填充.不幸的是我还没有能够解决问题,希望有人在这里可以启发我.

thx提前

Jon*_*rsi 9

你有基本的想法 - 你已经创建了结构,但你假设双精度值紧跟整数值后存储,而且通常是不正确的.你链接的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
Run Code Online (Sandbox Code Playgroud)

请注意,如果您有一个这样的派生类型的数组,为了覆盖在一个项目的最后一个元素和下一个项目的开头之间填充的情况,您还需要明确地测量它,并设置整体大小type - 该类型的一个成员的开头与下一个成员的开头之间的偏移量MPI_Type_create_resized.