我正在尝试使用Fortran代码(Gfortran)来使用OpenMP。它是基于粒子的代码,其中数组的索引可以对应于粒子或对。该代码使用派生类型为每个粒子存储多个矩阵。遇到需要使用存储在此派生类型中的矩阵的循环是很常见的。该矩阵可以由多个线程访问。循环还需要减少此派生类型中的元素。我目前必须编写一个临时数组才能进行此减少,然后我将派生类型的元素设置为等于此临时减少数组。如果不使用OpenMP,则不需要临时数组。
问题:是否可以对派生类型的元素进行缩减?我不认为我可以减少整个派生类型,因为我需要访问派生类型中的一些元素来完成工作,这意味着它需要共享。 (从阅读specification我了解到,当使用REDUCTION时,会创建每个列表项的私有副本。)
完整的最小工作示例如下。它可能更小,但我担心删除更多组件可能会简化问题。
PROGRAM TEST_OPEN_MP
USE, INTRINSIC :: iso_fortran_env
USE omp_lib
IMPLICIT NONE
INTEGER, PARAMETER :: dp = REAL64
INTEGER, PARAMETER :: ndim=3
INTEGER, PARAMETER :: no_partic=100000
INTEGER, PARAMETER :: len_array=1000000
INTEGER :: k, i, ii, j, jj
INTEGER, DIMENSION(1:len_array) :: pair_i, pair_j
REAL(KIND=dp), DIMENSION(1:len_array) :: pair_i_r, pair_j_r
REAL(KIND=dp), DIMENSION(1:no_partic) :: V_0
REAL(KIND=dp), DIMENSION(1:ndim,1:no_partic) :: disp, foovec
REAL(KIND=dp), DIMENSION(1:ndim,1:len_array) :: dvx
REAL(KIND=dp), DIMENSION(1:2*ndim,1:len_array):: vec
REAL(KIND=dp), DIMENSION(1:ndim) :: disp_ij,temp_vec1,temp_vec2
REAL(KIND=dp), DIMENSION(1:ndim,1:ndim) :: temp_ten1,temp_ten2
REAL(KIND=dp), DIMENSION(1:no_partic,1:ndim,1:ndim):: reduc_ten1
REAL(KIND=dp) :: sum_check1,sum_check2,cstart,cend
TYPE :: matrix_holder !<-- The derived type
REAL(KIND=dp), DIMENSION(1:ndim,1:ndim) :: mat1 !<-- The first element
REAL(KIND=dp), DIMENSION(1:ndim,1:ndim) :: mat2 !<-- The second element, etc.
END TYPE matrix_holder
TYPE(matrix_holder), DIMENSION(1:no_partic) :: matrix
! Setting "random" values to the arrays
DO k = 1, no_partic
CALL random_number(matrix(k)%mat1(1:ndim,1:ndim))
CALL random_number(matrix(k)%mat2(1:ndim,1:ndim))
END DO
CALL random_number(pair_i_r)
CALL random_number(pair_j_r)
CALL random_number(disp)
CALL random_number(vec)
CALL random_number(dvx)
CALL random_number(V_0)
disp = disp*10.d0
vec = vec*100.d0
dvx = dvx*200.d0
V_0 = V_0*10d0
pair_i = FLOOR(no_partic*pair_i_r)+1
pair_j = FLOOR(no_partic*pair_j_r)+1
! Doing the work
cstart = omp_get_wtime()
!$OMP PARALLEL DO DEFAULT(SHARED) &
!$OMP& PRIVATE(i,j,k,disp_ij,temp_ten1,temp_ten2,temp_vec1,temp_vec2,ii,jj), &
!$OMP& REDUCTION(+:foovec,reduc_ten1), SCHEDULE(static)
DO k= 1, len_array
i = pair_i(k)
j = pair_j(k)
disp_ij(1:ndim) = disp(1:ndim,i)-disp(1:ndim,j)
temp_vec1 = MATMUL(matrix(i)%mat2(1:ndim,1:ndim),&
vec(1:ndim,k))
temp_vec2 = MATMUL(matrix(j)%mat2(1:ndim,1:ndim),&
vec(1:ndim,k))
DO jj=1,ndim
DO ii = 1,ndim
temp_ten1(ii,jj) = -disp_ij(ii) * vec(jj,k)
temp_ten2(ii,jj) = disp_ij(ii) * vec(ndim+jj,k)
END DO
END DO
reduc_ten1(i,1:ndim,1:ndim)=reduc_ten1(i,1:ndim,1:ndim)+temp_ten1*V_0(j) !<--The temporary reduction array
reduc_ten1(j,1:ndim,1:ndim)=reduc_ten1(j,1:ndim,1:ndim)+temp_ten2*V_0(i)
foovec(1:ndim,i) = foovec(1:ndim,i) - temp_vec1(1:ndim)*V_0(j) !<--A generic reduction vector
foovec(1:ndim,j) = foovec(1:ndim,j) + temp_vec1(1:ndim)*V_0(i)
END DO
!$OMP END PARALLEL DO
cend = omp_get_wtime()
! Checking the results
sum_check1 = 0.d0
sum_check2 = 0.d0
DO i = 1,no_partic
matrix(i)%mat2(1:ndim,1:ndim)=reduc_ten1(i,1:ndim,1:ndim) !<--Writing the reduction back to the derived type element
sum_check1 = sum_check1+SUM(foovec(1:ndim,i))
sum_check2 = sum_check2+SUM(matrix(i)%mat2(1:ndim,1:ndim))
END DO
WRITE(*,*) sum_check1, sum_check2, cend-cstart
END PROGRAM TEST_OPEN_MP
我能想到的唯一其他选择是删除所有派生类型,并将其替换为示例中与reduc_ten1
类似的大型数组。
答案 0 :(得分:1)
不幸的是,你想要的是不可能的。至少如果我理解你的(对我来说很复杂!)代码是正确的。
问题是你有一个派生类型数组,每个派生类型都有一个数组。你不能参考它。
考虑这个玩具示例:
type t
real :: mat(3)
end type
integer, parameter :: n = 100, nk = 1000
type(t) :: parts(n)
integer :: i
real :: array(3,n,nk)
do k = 1, nk
array(:,:,nk) = k
end do
do i = 1, n
parts(i)%mat = 0
end do
!$omp parallel do reduction(+:parts%mat)
do k = 1, nk
do i = 1, n
parts(i)%mat = parts(i)%mat + array(:,i,nk)
end do
end do
!$omp end parallel do
end
英特尔Fortran提出了一个更具体的错误:
reduction6.f90(23): error #6159: A component cannot be an array if the encompassing structure is an array. [MAT]
!$omp parallel do reduction(+:parts%mat)
--------------------------------------^
reduction6.f90(23): error #7656: Subobjects are not allowed in this OpenMP* clause; a named variable must be specified. [PARTS]
!$omp parallel do reduction(+:parts%mat)
--------------------------------^
请记住,完全没有OpenMP,甚至不允许这样做:
parts%mat = 0
英特尔:
reduction6.f90(21): error #6159: A component cannot be an array if the encompassing structure is an array. [MAT]
gfortran:
Error: Two or more part references with nonzero rank must not be specified at (1)
你必须这样做:
do i = 1, n
parts(i)%mat = 0
end do
上述英特尔报告错误的原因非常相似。
实际上,reduce子句中不允许派生类型组件,只能使用变量名。这就是gfortran报告语法错误的原因。它不期望任何%
。英特尔再次提供了更清晰的错误消息。
但是有人可以解决这个问题,比如将它传递给子程序并在那里进行缩减。