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

このコードは実際には正しい結果をもたらします。しかし、最後のループでギャング/ベクトルの組み合わせを試すと、次のようになります。

!$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

結果はすべて間違って返されます。の要素のすべてではありませんが、ほとんどの合計が不完全であるように見えますoutvecreductionこれは、ギャングであろうとベクトルであろうと、どこに句を入れても当てはまります。場所を変更すると結果は変わりますが、正しい結果が得られることはありません。

簡単なテストで得た結果は次のようなものです。 matrixは 10x10 ですべて 1 で、invec1,2,3,...10 です。したがって、 のoutvec各要素は , 55 の要素の合計になるはずですinvec。コードのギャング/ベクトル バージョンを実行すると、 の各要素はoutvec55 ではなく 1 になります。正解は 55 です。これは、要素数が 90 を超えるまで機能し続けます。91 になると、 のすべての要素はoutvec4186 に等しくなるはずです。ただし、最後の 1 つだけで、残りはすべて 4095 (1 から 90 の合計) に等しくなります。要素数が多くなるほど値のばらつきが大きくなり、正解との乖離が大きくなります。

削減の仕組みがよくわかりません。誰でも説明できますか?

4

1 に答える 1

1

リダクション句は、リダクションが発生するループ、つまりベクトル ループ上にある必要があります。また、ここで「kernels」ディレクティブを使用することをお勧めします。これは、「parallel」が 2 つのループに対して 1 つのカーネル起動を作成し、「kernels」がループごとに 1 つずつ、2 つのカーネルを作成するためです。

例えば:

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 に答える