这是矩阵向量乘法的 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 的总和)。随着元素数量的增加,值的变化以及与正确答案的差异会变得更糟。
我显然不明白减少是如何工作的。谁能解释一下?