以下は、行列とベクトルの乗算の 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
このコードは実際には正しい結果をもたらします。しかし、最後のループでギャング/ベクトルの組み合わせを試すと、次のようになります。
!$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
各要素は , 55 の要素の合計になるはずですinvec
。コードのギャング/ベクトル バージョンを実行すると、 の各要素はoutvec
55 ではなく 1 になります。正解は 55 です。これは、要素数が 90 を超えるまで機能し続けます。91 になると、 のすべての要素はoutvec
4186 に等しくなるはずです。ただし、最後の 1 つだけで、残りはすべて 4095 (1 から 90 の合計) に等しくなります。要素数が多くなるほど値のばらつきが大きくなり、正解との乖離が大きくなります。
削減の仕組みがよくわかりません。誰でも説明できますか?