【问题标题】:Fortran90 derived types with mpi, alignment issue?带有 mpi 的 Fortran90 派生类型,对齐问题?
【发布时间】:2015-11-22 02:42:40
【问题描述】:

以下基本代码有问题:

program foo

  use mpi

  implicit none

  type bartype
     real(8) :: x
     integer :: i
  end type bartype

  integer :: mpi_bar_type

  integer ::                            &
       count=2,                         &
       blocklengths(2)=(/1,1/),         &
       types(2)=(/mpi_double_precision, &
                  mpi_integer/)
  integer(kind=mpi_address_kind) :: displs(2)
  type(bartype) :: bar, bararray(4)
  integer :: rank, ierr, i, test(4), addr0


  call mpi_init(ierr)
  call mpi_comm_rank(mpi_comm_world, rank, ierr)

  call mpi_get_address(bar, addr0)
  call mpi_get_address(bar%x, displs(1))
  call mpi_get_address(bar%i, displs(2))
  do i=1,2
     displs(i)=displs(i)-addr0
  enddo

  call mpi_type_create_struct(2,blocklengths,displs,types,mpi_bar_type,ierr)
  call mpi_type_commit(mpi_bar_type,ierr)

  bararray(:)%x=rank
  bararray(:)%i=rank
  test(:)=rank
  call mpi_bcast(test, 4, mpi_integer, 0, mpi_comm_world,ierr)
  call mpi_bcast(bararray, 4, mpi_bar_type, 0, mpi_comm_world,ierr)

  call mpi_finalize(ierr)

end program foo

我在调试器 (DDT) 中的派生类型 Bcast(使用 intelMPI 和 openMPI)处遇到段错误,据说这可能是对齐问题...

我已经看到this线程,问题似乎是一样的,但我仍然没有解决...

感谢您的帮助!

【问题讨论】:

  • (1) 一直使用implicit none; (2) use mpi 而不是 include mpif.h。一旦 (1) 和 (2) 完成,您会注意到 (3) 您没有为 mpi_cadna_double_st 定义类型(应该是整数)和 (4) displs 的类型是错误的(您应该使用integer(kind=mpi_address_kind) . 应该这样做。
  • 是的,我的示例代码非常糟糕。我更正了它(并按照我的要求编辑了我的帖子),但我仍然有一个段错误......
  • 更新后的代码现在差不多了,但addr0也需要integer(kind=mpi_address_kind);如果您在创建类型之前print *, displs,您会注意到这些值是无意义的,这很容易导致段错误,因为它们描述了内存访问。我真的很惊讶 gfortran 没有在那里发出警告。

标签: fortran mpi fortran90 memory-alignment derived-types


【解决方案1】:

试试这个:

program foo
  implicit none
  include 'mpif.h'

  type bartype
     real(8) :: x
     integer :: i
  end type bartype

  integer :: mpi_bar_type

  integer ::                            &
       count=4,                         &
       blocklengths(4)=(/1,1,1,1/),      &
       types(4)=(/MPI_LB,mpi_double_precision, &
                  mpi_integer,MPI_UB/)
  integer(kind=mpi_address_kind) :: displs(4)
  type(bartype) :: bararray(4)
  integer :: rank, ierr, i, test(4)


  call mpi_init(ierr)
  call mpi_comm_rank(mpi_comm_world, rank, ierr)

  call mpi_get_address(bararray(1), displs(1))
  call mpi_get_address(bararray(1)%x, displs(2))
  call mpi_get_address(bararray(1)%i, displs(3))
  call mpi_get_address(bararray(2), displs(4))

  do i=4,1,-1
     displs(i)=displs(i)-displs(1)
  enddo

  call mpi_type_create_struct(4,blocklengths,displs,types,mpi_bar_type,ierr)
  call mpi_type_commit(mpi_bar_type,ierr)

  bararray(:)%x=rank
  bararray(:)%i=rank
  test(:)=rank
  print *, "before", bararray

  call mpi_bcast(test, 4, mpi_integer, 0, mpi_comm_world,ierr)
  call mpi_bcast(bararray, 4, mpi_bar_type, 0, mpi_comm_world,ierr)

  print *, "after", bararray

  call mpi_finalize(ierr)

end program foo

注意使用MPI_LBMPI_UB 作为结构的附加虚构成员。这是为了确保类型的范围是正确的。 我不完全确定这是根据标准推荐的方法,但它一直对我有用。 据我所知,标准规定在您的类型定义中添加 bind(C)sequence,但即使如此,我也不确定设置类型的上限是否有效,因为您将遇到对齐问题 I怀疑。


编辑:在关于确实已弃用的 MPI_LB 和 MPI_UB 的各种评论之后,并仔细重新阅读该标准,我想以下工作应该是合规的。

program foo
  implicit none
  include 'mpif.h'

  type bartype
     real(8) :: x
     integer :: i
  end type bartype

  integer :: tmp_type, bar_type

  integer ::                            &
       count=4,                         &
       blocklengths(2)=(/1,1/),         &
       types(2)=(/mpi_double_precision, &
                  mpi_integer/)
  integer(kind=mpi_address_kind) :: displs(2), lb, extent
  type(bartype) :: bararray(4)
  integer :: rank, ierr, i, test(4)


  call mpi_init(ierr)
  call mpi_comm_rank(mpi_comm_world, rank, ierr)

  call mpi_get_address(bararray(1)%x, displs(1))
  call mpi_get_address(bararray(1)%i, displs(2))
  call mpi_get_address(bararray(1), lb)
  call mpi_get_address(bararray(2), extent)

  do i=1,2
     displs(i)=displs(i)-lb
  enddo
  extent=extent-lb
  lb=0

  call mpi_type_create_struct(2,blocklengths,displs,types,tmp_type,ierr)
  call mpi_type_commit(tmp_type,ierr)
  call mpi_type_create_resized(tmp_type,lb,extent,bar_type,ierr)
  call mpi_type_free(tmp_type,ierr)
  call mpi_type_commit(bar_type,ierr)

  bararray(:)%x=rank
  bararray(:)%i=rank
  test(:)=rank
  print *, "before", bararray

  call mpi_bcast(test, 4, mpi_integer, 0, mpi_comm_world,ierr)
  call mpi_bcast(bararray, 4, bar_type, 0, mpi_comm_world,ierr)

  print *, "after", bararray

  call mpi_type_free(bar_type,ierr)
  call mpi_finalize(ierr)

end program foo

【讨论】:

  • @JonathanDursi 我知道他们是,但标准并不太清楚(至少对我而言)它们被弃用的实际原因,以及实现其目的的有效方式不使用它们...你能告诉我吗,那太好了。
  • 你是对的,你通常需要这些信息 - 例如,如果 OP 想要创建一个数组,那么几乎肯定会在最后填充 - 即使那不是这里的问题。当前推荐的方法是使用MPI_Type_create_resized。至于弃用的原因,我真的不能说;我认为新风格更简洁,但大概有一些更强有力的实施理由来进行更改。
  • “新”(自 1998 年以来)方式:在创建结构类型时不要设置 MPI_LB 和 MPI_UB,而是使用 MPI_Type_create_resized。您仍然使用 double 和 int 创建两个类型的结构,然后调整该类型的大小以获得所需的下限和(从范围派生)上限。不过,没有人会拿走 MPI_LB 和 MPI_UB,所以你的方法至少还能正常工作 2 年。
  • @Gilles - 哦,原来只有一个discussion on the OpenMPI mailing list 关于弃用的原因;像这样对 UB/LB 进行“硬编码”对于单个结构来说是非常好的,但是一旦您将如此定义的类型用作新数据类型中的子类型,就会开始产生令人惊讶的行为。
猜你喜欢
  • 2012-02-11
  • 1970-01-01
  • 2012-12-21
  • 2012-02-03
  • 2018-11-30
  • 2014-12-03
  • 2014-08-08
  • 2012-10-23
相关资源
最近更新 更多