使用MPI派生数据类型

时间:2014-06-18 00:51:57

标签: types fortran 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)

通过更改相应的声明和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不正确,因为可能存在未考虑的额外填充。不幸的是我还没有能够解决问题,希望有人能够启发我。

事先提前

1 个答案:

答案 0 :(得分:8)

你有基本的想法 - 你已经创建了结构,但是你假设双精度值紧跟在整数值之后,并且通常不正确。你联系的Hristo的回答在C中给出了一个很好的答案。

问题是编译器通常会align为您提供数据结构字段。大多数系统可以读取/写入在内存中对齐的值,这比它们执行非对齐访问要快得多,如果它们可以执行那些。通常,要求是对齐是在元素大小上;这是一个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