Fortran 使用OpenMP减少阵列的最佳方法是什么?

Fortran 使用OpenMP减少阵列的最佳方法是什么?,fortran,openmp,Fortran,Openmp,我正在使用OpenMP和Fortran。我已经把我的用例归结为一个非常简单的例子。我有一个自定义派生类型的对象数组,每个对象都包含一个大小不同的数组。我希望确保无论循环中发生什么,我都会对向量对象的所有值数组组件应用缩减: program main implicit none integer :: i type vector real,allocatable :: values(:) end type vector type(vector) :: vecto

我正在使用OpenMP和Fortran。我已经把我的用例归结为一个非常简单的例子。我有一个自定义派生类型的对象数组,每个对象都包含一个大小不同的数组。我希望确保无论循环中发生什么,我都会对向量对象的所有
数组组件应用缩减:

program main

  implicit none

  integer :: i

  type vector
     real,allocatable :: values(:)
  end type vector

  type(vector) :: vectors(3)

  allocate(vectors(1)%values(3))
  vectors(1)%values = 0

  allocate(vectors(2)%values(6))
  vectors(2)%values = 0

  allocate(vectors(3)%values(9))
  vectors(3)%values = 0

  !$OMP PARALLEL REDUCTION(+:vectors%values)

  !$OMP DO

  do i=1,1000
     vectors(1)%values = vectors(1)%values + 1
     vectors(2)%values = vectors(2)%values + 2
     vectors(3)%values = vectors(3)%values + 3
  end do

  !$OMP END DO

  !$OMP END PARALLEL

  print*,sum(vectors(1)%values)
  print*,sum(vectors(2)%values)
  print*,sum(vectors(3)%values)

end program main
在这种情况下,
减少(+:vectors%values)
不起作用,因为我得到以下错误:

test2.f90(22): error #6159: A component cannot be an array if the encompassing structure is an array.   [VALUES]
  !$OMP PARALLEL REDUCTION(+:vectors%values)
-------------------------------------^
test2.f90(22): error #7656: Subobjects are not allowed in this OpenMP* clause; a named variable must be specified.   [VECTORS]
  !$OMP PARALLEL REDUCTION(+:vectors%values)
-----------------------------^
compilation aborted for test2.f90 (code 1)
我尝试重载向量类型的
+
的含义,然后指定
减少(+:vectors)
,但仍然得到:

test.f90(43): error #7621: The data type of the variable is not defined for the operator or intrinsic specified on the OpenMP* REDUCTION clause.   [VECTORS]
  !$OMP PARALLEL REDUCTION(+:vectors)
-----------------------------^
建议使用什么方法来处理此类派生类型并使约简生效

仅供参考,在没有OpenMP的情况下编译时,正确的输出是

3000.000    
12000.00    
27000.00  

这不仅仅是OpenMP问题,如果
是可分配数组组件,则不能将
向量%values
作为一个实体引用,因为Fortran 2003的规则禁止这样做。这是因为这样的数组在内存中不会有任何规则的跨步,所以可分配组件存储在随机地址中

如果包围数组的元素数很小,可以这样做

  !$OMP PARALLEL REDUCTION(+:vectors(1)%values,vectors(2)%values,vectors(3)%values)

  !$OMP DO

  do i=1,1000
     vectors(1)%values = vectors(1)%values + 1
     vectors(2)%values = vectors(2)%values + 2
     vectors(3)%values = vectors(3)%values + 3
  end do

  !$OMP END DO

  !$OMP END PARALLEL
否则,您必须进行另一个循环,比如说
j
,并使reduce正好
向量(j)%value

如果编译器不接受reduction子句中的结构组件(必须研究最新的标准,看看它是否被放宽),您可以采取变通办法

  !$OMP PARALLEL
  do j = 1, size(vectors)
    call aux(vectors(j)%values)
  end do
  !$OMP END PARALLEL

contains
  subroutine aux(v)
    real :: v(:)

    !$OMP DO REDUCTION(+:v)
    do i=1,1000
      v = v + j
    end do
    !$OMP END DO
  end subroutine
关联或指针会更简单,但它们也不允许使用。

作为替代,您可以始终使用临时数组和关键部分实现自己的缩减:

program main

  implicit none

  integer :: i

  type vector
     real,allocatable :: values(:)
  end type vector

  type(vector) :: vectors(3)
  type(vector),allocatable :: tmp(:)

  allocate(vectors(1)%values(3))
  vectors(1)%values = 0

  allocate(vectors(2)%values(6))
  vectors(2)%values = 0

  allocate(vectors(3)%values(9))
  vectors(3)%values = 0

  !$OMP PARALLEL PRIVATE(TMP)

  ! Use a temporary array to hold the local sum
  allocate( tmp(size(vectors)) )
  do i=1,size(tmp)
    allocate( tmp(i)%values( size(vectors(i)%values )) )
    tmp(i)%values = vectors(i)%values
  enddo ! i

  !$OMP DO

  do i=1,1000
     tmp(1)%values = tmp(1)%values + 1
     tmp(2)%values = tmp(2)%values + 2
     tmp(3)%values = tmp(3)%values + 3
  end do

  !$OMP END DO

  ! Get the global sum one thread at a time
  !$OMP CRITICAL
  vectors(1)%values = vectors(1)%values + tmp(1)%values
  vectors(2)%values = vectors(2)%values + tmp(2)%values
  vectors(3)%values = vectors(3)%values + tmp(3)%values
  !$OMP END CRITICAL

  deallocate(tmp)
  !$OMP END PARALLEL

  print*,sum(vectors(1)%values)
  print*,sum(vectors(2)%values)
  print*,sum(vectors(3)%values)

end program main

通过在
向量的所有元素上循环,可以更有效地排列此代码段。然后,
tmp
可以是标量

向量(1)%value
似乎未被识别为有效语法。如果我尝试用gfortran 4.9.3编译它,我会在OpenMP变量列表中得到
语法错误。这对您有效吗?如果有效,您使用什么编译器?这是因为语法检查器需要变量名而不是表达式。您可以在gfortran-4.8中使用associate作为变通方法,但更高版本不喜欢它。@请参阅使用内部过程的变通方法。