【问题标题】:derived data types with MPI使用 MPI 的派生数据类型
【发布时间】:2014-08-08 03:44:03
【问题描述】:

我正在学习 Fortran 中的 BCASTing 数据类型,并有一个代码从终端获取两个值并在每个进程上显示它们。对于 integer/integer 和 integer/real 类型的组合 value1/value2 这是有效的,但是对于组合 integer/real*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)

可以通过更改相应的声明和旧类型来检查整数/整数和整数/实数是否正常工作。 integer/real*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 是不正确的,因为可能存在未考虑的额外填充。不幸的是我还没有解决这个问题,希望这里的人能启发我。

提前谢谢

【问题讨论】:

    标签: types fortran mpi


    【解决方案1】:

    您的基本想法是正确的 - 您已经创建了结构,但您假设双精度值紧跟整数值存储,这通常是不正确的。您链接到的 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

    【讨论】:

    • +1 感谢您的澄清。我已经在玩 MPI_GET_ADDRESS 但还没有让它工作。你的例子效果很好!
    猜你喜欢
    • 2012-02-03
    • 2012-02-11
    • 2014-12-03
    • 2017-05-27
    • 2018-02-26
    • 2019-01-20
    • 2012-11-27
    • 1970-01-01
    相关资源
    最近更新 更多