派生数据类型作为MPI窗口的基准指针

时间:2016-09-02 13:42:30

标签: fortran mpi derived-types

我想使用带有MPI3共享内存的派生数据类型。 给定以下派生数据类型:

type ::pve_data
  type(pve_data), pointer :: next => NULL()
  real*8        , pointer :: array(:) => NULL()
end type pve_data

初始化为:

type(pve_data) :: pve_grid
allocate(pve_grid%array(10))
pve_grid%array = 0.0d0

使用pve_grid完成一些计算后,我想创建一个MPI_WIN_ALLOCATE_SHARED的共享内存窗口,其基数应为pve_grid。 (也许MPI_WIN_CREATE_DYNAMIC可以替代,但我需要通过共享内存来表现)

1)直到现在我只使用原始数据类型或那些数组作为基本指针来创建窗口。派生数据类型也可以用作basepointer吗?或者我是否需要为派生数据类型的每个组件创建一个窗口,这是一个原始变量?

2)是否可以使用已经“使用过的”变量(在这种情况下为pve_grid)作为basepointer?或者我是否需要使用新的pve_data作为基准指针并将值从pve_grid复制到它?

修改 我知道,使用OpenMP方法而不是MPI共享内存会更容易。但我想故意尝试一种仅限MPI的方法,以提高我的MPI能力。

EDIT2 (05.09.16) 我取得了一些进展,并且能够使用共享内存,其中basepointer是一个简单的整数变量。但是当我想使用派生数据类型作为创建窗口的basepointer时,我仍然有一个问题(出于测试目的,我更改了它的定义 - 请参阅下面的sharedStructMethod.f90)。编译器和执行不会抛出任何错误......远程访问对派生的数据类型组件没有任何影响:write显示旧值,它已由父级初始化。后续代码显示我当前的状态。我使用了在执行期间生成新进程的可能性:父进程创建窗口,子进程对其执行更改。我希望产生的过程不会给调试工作带来麻烦,我只是为我的项目添加了它。 (并且下次我会改变真正的* 8以符合标准)。

派生数据类型声明(sharedStructMethod.f90)

  module sharedStructMethod

      REAL*8, PARAMETER      :: prec=1d-13
      INTEGER, PARAMETER     :: masterProc = 0
      INTEGER, PARAMETER     :: SLAVE_COUNT = 2
      INTEGER, PARAMETER     :: CONSTSIZE = 10
      !Struct-Definition    
         type :: vertex
            INTEGER, Dimension(3) :: coords
         end type vertex

         type :: pve_data
            real(kind(prec)), pointer   :: intensity(:) => NULL()
            logical, pointer            :: flag => NULL()
            type(vertex), pointer       :: vertices(:) => NULL()
         end type pve_data
      end module sharedStructMethod

用户执行的父进程(sharedStruct.f90)的声明。

      PROGRAM sharedStruct
     USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_PTR, C_F_POINTER
     USE mpi
     USE sharedStructMethod
     IMPLICIT NONE
     type(pve_data) :: pve_grid
     integer        :: ierror
     integer        :: myRank, numProcs
     INTEGER        :: childComm
     INTEGER        :: childIntracomm
     integer        :: i
     INTEGER(KIND=MPI_ADDRESS_KIND) :: memSize
     INTEGER                        :: dispUnit
     TYPE(C_PTR)                    :: basePtr
     INTEGER                        :: win
     TYPE(pve_data), POINTER        :: shared_data

     call MPI_INIT(ierror)
     memSize = sizeof(pve_grid)
     dispUnit = 1
     CALL MPI_COMM_SPAWN("sharedStructWorker.x", MPI_ARGV_NULL, SLAVE_COUNT, MPI_INFO_NULL, masterProc,MPI_COMM_SELF, childComm,MPI_ERRCODES_IGNORE, ierror);
     CALL MPI_INTERCOMM_MERGE(childComm, .false., childIntracomm, ierror)
     CALL MPI_WIN_ALLOCATE_SHARED(memSize, dispUnit, MPI_INFO_NULL, childIntracomm, basePtr, win, ierror)
     CALL C_F_POINTER(basePtr, shared_data)
     CALL MPI_WIN_LOCK(MPI_LOCK_EXCLUSIVE, masterProc,0,win,ierror)

     allocate(shared_data%intensity(CONSTSIZE))
     allocate(shared_data%vertices(CONSTSIZE))
     allocate(shared_data%flag)
     shared_data%intensity = -1.0d0
     DO i =1,CONSTSIZE
        shared_data%vertices(i)%coords(1) = -1
        shared_data%vertices(i)%coords(2) = -2
        shared_data%vertices(i)%coords(3) = -3
     END DO
     shared_data%flag = .true.

     CALL MPI_WIN_UNLOCK(masterProc, win, ierror)
     CALL MPI_BARRIER(childIntracomm, ierror)
     CALL MPI_BARRIER(childIntracomm, ierror)
     WRITE(*,*) "After: Flag ",shared_data%flag,"intensity(1): ",shared_data%intensity(1)
     call mpi_finalize(ierror)
  END PROGRAM sharedStruct

最后但并非最不重要的:子进程的声明,它在运行时由parent-proc自动生成,并确实更改了窗口内容(sharedStructWorker.f90)

PROGRAM sharedStructWorker
USE mpi
  USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_PTR, C_F_POINTER
  USE sharedStructMethod
  IMPLICIT NONE

  INTEGER                   :: ierror
  INTEGER                   :: myRank, numProcs
  INTEGER                   :: parentComm
  INTEGER                   :: parentIntracomm
  TYPE(C_PTR)               :: pveCPtr
  TYPE(pve_data), POINTER   :: pve_gridPtr
  INTEGER                   :: win
  INTEGER(KIND=MPI_ADDRESS_KIND)    :: sizeOfPve
  INTEGER                   :: dispUnit2

  CALL MPI_INIT(ierror)
  CALL MPI_COMM_GET_PARENT(parentComm, ierror)
  CALL MPI_INTERCOMM_MERGE(parentComm, .true., parentIntracomm, ierror)
  sizeOfPve = 0_MPI_ADDRESS_KIND
  dispUnit2 = 1
  CALL MPI_WIN_ALLOCATE_SHARED(sizeOfPve,dispUnit2, MPI_INFO_NULL, parentIntracomm, pveCPtr, win, ierror)
  CALL MPI_WIN_SHARED_QUERY(win, masterProc, sizeOfPve, dispUnit2, pveCPtr, ierror)
  CALL C_F_POINTER(pveCPtr, pve_gridPtr)

  CALL MPI_BARRIER(parentIntracomm, ierror)
  CALL MPI_WIN_LOCK(MPI_LOCK_EXCLUSIVE,masterProc,0,win,ierror)
  pve_gridPtr%flag = .false.
  pve_gridPtr%intensity(1) = 42
  CALL MPI_WIN_UNLOCK(masterProc, win, ierror)
  CALL MPI_BARRIER(parentIntracomm, ierror)
  CALL MPI_FINALIZE(ierror)
  END PROGRAM sharedStructWorker

编译:

mpiifort   -c  sharedStructMethod.f90
mpiifort   -o  sharedStructWorker.x sharedStructWorker.f90 sharedStructMethod.o
mpiifort   -o  sharedStruct.x sharedStruct.f90 sharedStructMethod.o

这是正确的方法还是我需要为派生数据类型pve_data的每个组件创建一个带有自己的窗口的共享内存块,这只是一个指针?谢谢你的帮助!

编辑 10/09/2016,解决方案: 评论中的解释。解决问题的一种方法是为父组件和子组件工作的每个组件生成一个窗口。对于更复杂的派生数据类型,实现起来很快就会变得乏味,但似乎没有不同的选择。

1 个答案:

答案 0 :(得分:1)

我认为这主要包括在内:MPI Fortran code: how to share data on node via openMP?

因此,就1)而言,您可以使用Fortran派生类型的基本指针。但是,2)的答案是MPI_Win_alloc_shared 向您返回存储 - 您无法重用现有存储。鉴于您有一个链表,我不知道如何在原则上将其转换为共享窗口。为了能够使用返回的存储,拥有一个pve_data对象数组要简单得多 - 您必须将它们连续存储在返回的数组中,因此链接它们似乎不会添加任何有用的东西。

我可能在这里误解了 - 如果您只想在窗口中远程访问列表的头部,那么应该没问题。