作为 MPI 基指针的派生数据类型 window
Derived datatype as basepointer for MPI window
我想将派生数据类型与 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
创建一个共享内存 window,其基数应该是 pve_grid
。 (也许 MPI_WIN_CREATE_DYNAMIC
是替代方案,但我需要通过共享内存实现性能)
1) 到目前为止,我只是使用原始数据类型或这些数据类型的数组作为 window 创建的基指针。派生数据类型也可以用作基指针吗?或者我是否需要为派生数据类型的每个组件创建一个 window,这是一个原始变量?
2) 是否可以使用已经 "used" 的变量(在本例中为 pve_grid
)作为基指针?或者我是否需要使用新的 pve_data
作为基指针并将值从 pve_grid
复制到它?
编辑
我知道,使用 OpenMP 方法而不是 MPI 共享内存会更容易。但我想有意尝试一种仅使用 MPI 的方法,以提高我的 MPI 能力。
EDIT2 (05.09.16)
我取得了一些进展并且能够使用共享内存,其中基指针是一个简单的整数变量。但是当我想使用派生数据类型作为 window-creation 的基指针时,我仍然遇到问题(出于测试目的,我更改了它的定义 - 请参阅下面的 sharedStructMethod.f90)。编译器和执行不会抛出任何错误......远程访问对派生数据类型组件没有任何影响:写入显示旧值,已由父级初始化。随后的代码显示了我当前的状态。我使用了在执行期间产生新进程的可能性:父进程创建 window 并且子进程在其上执行更改。我希望生成过程不会给调试带来麻烦,我只是为我的项目添加了它。 (下次我将更改 real*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
最后但同样重要的是:子进程的声明,它在运行时由父进程自动产生,并且确实改变了window内容(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 的每个组件创建一个共享内存块 window,它只是一个指针?感谢您的帮助!
编辑 10/09/2016,解决方案:
评论中的解释。解决该问题的一种方法是为父子工作的每个组件生成一个 window。对于更复杂的派生数据类型,实现起来很快就会变得乏味,但似乎没有其他选择。
我认为这主要涵盖在这里:MPI Fortran code: how to share data on node via openMP?
因此,就 1) 而言,您可以拥有作为 Fortran 派生类型的基指针。但是,2) 的答案是 MPI_Win_alloc_shared returns 存储给您 - 您不能重复使用现有存储。鉴于您有一个链接列表,我什至在原则上都看不到如何将其转换为共享 window 。为了能够使用返回的存储,拥有一个 pve_data 对象数组会简单得多 - 您将不得不将它们连续存储在返回的数组中,因此链接它们似乎不会添加任何有用的东西。
我可能在这里误解了 - 如果您只想在 window 中远程访问列表的头部,那应该没问题。
我想将派生数据类型与 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
创建一个共享内存 window,其基数应该是 pve_grid
。 (也许 MPI_WIN_CREATE_DYNAMIC
是替代方案,但我需要通过共享内存实现性能)
1) 到目前为止,我只是使用原始数据类型或这些数据类型的数组作为 window 创建的基指针。派生数据类型也可以用作基指针吗?或者我是否需要为派生数据类型的每个组件创建一个 window,这是一个原始变量?
2) 是否可以使用已经 "used" 的变量(在本例中为 pve_grid
)作为基指针?或者我是否需要使用新的 pve_data
作为基指针并将值从 pve_grid
复制到它?
编辑 我知道,使用 OpenMP 方法而不是 MPI 共享内存会更容易。但我想有意尝试一种仅使用 MPI 的方法,以提高我的 MPI 能力。
EDIT2 (05.09.16) 我取得了一些进展并且能够使用共享内存,其中基指针是一个简单的整数变量。但是当我想使用派生数据类型作为 window-creation 的基指针时,我仍然遇到问题(出于测试目的,我更改了它的定义 - 请参阅下面的 sharedStructMethod.f90)。编译器和执行不会抛出任何错误......远程访问对派生数据类型组件没有任何影响:写入显示旧值,已由父级初始化。随后的代码显示了我当前的状态。我使用了在执行期间产生新进程的可能性:父进程创建 window 并且子进程在其上执行更改。我希望生成过程不会给调试带来麻烦,我只是为我的项目添加了它。 (下次我将更改 real*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
最后但同样重要的是:子进程的声明,它在运行时由父进程自动产生,并且确实改变了window内容(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 的每个组件创建一个共享内存块 window,它只是一个指针?感谢您的帮助!
编辑 10/09/2016,解决方案: 评论中的解释。解决该问题的一种方法是为父子工作的每个组件生成一个 window。对于更复杂的派生数据类型,实现起来很快就会变得乏味,但似乎没有其他选择。
我认为这主要涵盖在这里:MPI Fortran code: how to share data on node via openMP?
因此,就 1) 而言,您可以拥有作为 Fortran 派生类型的基指针。但是,2) 的答案是 MPI_Win_alloc_shared returns 存储给您 - 您不能重复使用现有存储。鉴于您有一个链接列表,我什至在原则上都看不到如何将其转换为共享 window 。为了能够使用返回的存储,拥有一个 pve_data 对象数组会简单得多 - 您将不得不将它们连续存储在返回的数组中,因此链接它们似乎不会添加任何有用的东西。
我可能在这里误解了 - 如果您只想在 window 中远程访问列表的头部,那应该没问题。