0

这是矩阵向量乘法的 Fortran 子程序。它可能在许多方面都过时且效率低下,但现在我只是想让它与 OpenACC 指令一起使用,并且我试图弄清楚减少是如何工作的:

subroutine matrmult(matrix,invec,outvec,n)

integer:: n
real*8, intent(in):: matrix(n,n), invec(n)
real*8, intent(out) :: outvec(n)
real*8 :: tmpmat(n,n)
real*8 :: tmpscl

integer :: i,j,k

!$acc declare create(matrix, invec, outvec, tmpmat)

outvec = 0.d0

!$acc update device(matrix, invec, tmpmat, outvec)

!$acc parallel

!$acc loop gang
do j=1,n
!$acc loop vector
  do i=1,n
    tmpmat(i,j) = matrix(i,j)*invec(j)
  enddo
enddo

!$acc loop vector reduction(+:tmpsclr)
do j=1,n
  tmpsclr = 0.d0
  do i=1,n
    tmpsclr = tmpsclr+tmpmat(j,i)
  enddo
  outvec(j) = tmpsclr
enddo

!$acc end parallel

!$acc update host(outvec)

end subroutine

这段代码实际上给出了正确的结果。但是当我在最后一个循环上尝试一个 gang/vector 组合时,就像这样:

!$acc loop gang reduction(+:tmpsclr)
do j=1,n
  tmpsclr = 0.d0
!$acc loop vector
  do i=1,n
    tmpsclr = tmpsclr+tmpmat(j,i)
  enddo
  outvec(j) = tmpsclr
enddo

结果回来都错了。看起来,对于 的大多数(但不是全部)元素的求和是不完整的outvec。无论我将reduction条款放在哪里,无论是帮派还是向量,都是这种情况。更改位置会更改结果,但永远不会给出正确的结果。

我在一个简单的测试中得到的结果如下。 matrix是 10x10 和全 1,并且invec是 1,2,3,...10。所以outvec每个元素都应该是invec, 55 中元素的总和。如果我运行代码的 gang/vector 版本,每个元素outvec都是 1,而不是 55。如果我用向量进行归约,那么我得到了正确的答案,55。这将继续有效,直到我超过 90 个元素。当我到达 91 时,每个元素都outvec应该等于 4186。但只有最后一个是,其余的都等于 4095(1 到 90 的总和)。随着元素数量的增加,值的变化以及与正确答案的差异会变得更糟。

我显然不明白减少是如何工作的。谁能解释一下?

4

1 回答 1

1

归约子句需要在发生归约的循环上,即向量循环。我还建议在这里使用“kernels”指令,因为“parallel”将为两个循环创建一个内核启动,而“kernels”将创建两个内核,每个循环一个。

例如:

subroutine foo(n,matrix,invec,outvec)
integer n
real*8, intent(in) :: matrix(n,n)
real*8, intent(in) :: invec(n)
real*8, intent(out) :: outvec(n)
real*8 :: tmpmat(n,n)
real*8 :: tmpscl

integer :: i,j,k

!$acc declare create(matrix, invec, outvec, tmpmat)

outvec = 0.d0

!$acc update device(matrix, invec, tmpmat, outvec)

!$acc kernels

!$acc loop gang
do j=1,n
!$acc loop vector
  do i=1,n
    tmpmat(i,j) = matrix(i,j)*invec(j)
  enddo
enddo

!$acc loop gang
do j=1,n
  tmpsclr = 0.d0
!$acc loop vector reduction(+:tmpsclr)
  do i=1,n
    tmpsclr = tmpsclr+tmpmat(j,i)
  enddo
  outvec(j) = tmpsclr
enddo

!$acc end kernels

!$acc update host(outvec)

end subroutine foo
% pgf90 -c -acc -Minfo=accel test2.f90
foo:
     11, Generating create(matrix(:,:),invec(:),outvec(:))
     15, Generating update device(outvec(:),tmpmat(:,:),invec(:),matrix(:,:))
     20, Loop is parallelizable
     22, Loop is parallelizable
         Accelerator kernel generated
         Generating Tesla code
         20, !$acc loop gang, vector(4) ! blockidx%y threadidx%y
         22, !$acc loop gang, vector(32) ! blockidx%x threadidx%x
     28, Loop is parallelizable
         Accelerator kernel generated
         Generating Tesla code
         28, !$acc loop gang ! blockidx%x
         31, !$acc loop vector(128) ! threadidx%x
             Sum reduction generated for tmpsclr
     31, Loop is parallelizable
     39, Generating update host(outvec(:))

希望这会有所帮助,垫

于 2015-11-23T22:36:36.163 回答