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

时间:2015-07-19 13:57:52

标签: fortran openmp

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

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

在这种情况下,REDUCTION(+: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)

我尝试为矢量类型重载+的含义,然后指定REDUCTION(+: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  

2 个答案:

答案 0 :(得分:3)

这不仅仅是OpenMP问题,如果vectors%values是可分配的数组组件,则不能将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并使缩减仅vectors(j)%values

如果编译器不接受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

关联或指针会更简单,但也不允许使用它们。

答案 1 :(得分:0)

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

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

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