0

次のコードでは、変数「aa」をプライベートとして渡すと、結果が悪くなります。コードは投稿された方法で正常に動作しますが、行を置き換えると

   !$OMP PARALLEL PRIVATE(iii,iter,y,i,yt) SHARED(bb)

   !$OMP PARALLEL PRIVATE(aa,iter,y,i,yt) SHARED(bb)

コードが正しく機能していません。

     !!!!!!!! module 
      module common
      use iso_fortran_env
      implicit none
      integer,parameter:: dp=real64
      real(dp):: aa,bb

       contains

      subroutine evolve(y,yevl)
      implicit none
      integer(dp),parameter:: id=2
      real(dp),intent(in):: y(id)
      real(dp),intent(out):: yevl(id)
        yevl(1)=y(2)+1.d0-aa*y(1)**2
        yevl(2)=bb*y(1)
      end subroutine evolve

      end module common

      use common
      implicit none
      integer(dp):: iii,iter,i
      integer(dp),parameter:: id=2
      real(dp),allocatable:: y(:),yt(:)
      integer(dp):: OMP_GET_THREAD_NUM, IXD

       allocate(y(id)); allocate(yt(id)); y=0.d0; yt=0.d0; bb=0.3d0
       !$OMP PARALLEL PRIVATE(iii,iter,y,i,yt) SHARED(bb)
         IXD=OMP_GET_THREAD_NUM()
       !$OMP DO
        do iii=1,20000; print*,iii  !! EXPECTED THREADS TO BE OF 5000 ITERATIONS EACH
          aa=1.d0+dfloat(iii-1)*0.4d0/2000.d0
            loop1: do iter=1,10 !! THE INITIAL CONDITION LOOP
               call random_number(y)!! RANDOM INITIALIZATION OF THE VARIABLE
                loop2: do i=1,70000  !! ITERATION OF THE SYSTEM
                    call evolve(y,yt)
                    y=yt
                enddo loop2     !! END OF SYSTEM ITERATION
              write(IXD+1,*)aa,yt  !!! WRITING FILE CORRESPONDING TO EACH THREAD
            enddo loop1 !!INITIAL CONDITION ITERATION DONE
         enddo
        !$OMP ENDDO
        !$OMP END PARALLEL
        end

問題は何ですか?「 iii」から「aa 」を生成するとうまくいきますが、それをプライベート変数として渡すとうまくいきません。コメントや提案をお寄せいただきありがとうございます。

4

2 に答える 2

4

aaモジュール変数です。モジュール変数は共有 (デフォルト) またはthreadprivate. OpenMP 標準ドキュメントの例 A.32.2f は、構造体の動的スコープでモジュール変数がアクセスされる場合、元の変数またはプライベート スレッド コピーがアクセスされるかどうかが未指定であることを示しています。threadprivate変数は、並列領域のレキシカル スコープ内で使用されるかどうかに関係なく、常にスレッド ローカル ストレージに格納されるため、これは当てはまりません。

モジュール変数をプライベートとして宣言し、サブルーチンにアクセスするとどうなるかについては、多くのシナリオがあります。何が起こる可能性が最も高いかは、コンパイラがコードに対して行う分析の種類によって異なります。一部のコンパイラは、モジュール サブルーチンが並列領域内でのみ呼び出され、aa各スレッドのプライベート コピーを参照することを検出する場合があります。他のコンパイラは、元のモジュール変数に常にアクセスすることを決定する場合があります。一方、サブルーチンが呼び出し元のサブルーチンでインライン化される場合、呼び出し元のコンテキストで使用されているものと同じものを参照する可能性があります (たとえば、宣言されaaている場合のプライベート バージョン) 。aaprivate

デフォルトの最適化レベルでgfortran処理する方法の例を次に示します。PRIVATE(iii,aa,iter,y,i,yt)

; aa is declared as a global symbol in the BSS section
    .globl  __common_MOD_aa
    .bss
    .align 8
    .type   __common_MOD_aa, @object
    .size   __common_MOD_aa, 8
__common_MOD_aa:
    .zero   8

; Here is how evolve accesses aa
    ...
    movsd   __common_MOD_aa(%rip), %xmm2
    ...

; Here is how the assignment to aa is done inside the parallel region
    ...
    movsd   %xmm0, -72(%rbp)
    ...

privateaaは自動変数として実装され、モジュールevolveの値を使用しながら、スレッドのスタックに格納されaaます。したがって、この演算子:

aa=1.d0+dfloat(iii-1)*0.4d0/2000.d0

aaスレッド内の値のみを変更し、並列領域の外側のevolve元の値を使用します。aa

高い最適化レベルでは、並列領域に-O3 gfortranインラインevolve化し、...

...
mulsd   __common_MOD_aa(%rip), %xmm2
...

インライン化されたコードはaa、モジュール内の のグローバル値も参照します。つまり、動作は 2 つの最適化レベル間で一貫しています。

同じことがインテル Fortran にも当てはまります。

正しいアプローチは、あると宣言しaa、それを句に入れないことです。threadprivateprivate

module common
use iso_fortran_env
implicit none
integer,parameter:: dp=real64
real(dp):: aa,bb
!$OMP THREADPRIVATE(aa)
...
 !$OMP PARALLEL PRIVATE(iii,iter,y,i,yt) SHARED(bb)
   IXD=OMP_GET_THREAD_NUM()
   !$OMP DO
   do iii=1,20000; print*,iii  !! EXPECTED THREADS TO BE OF 5000 ITERATIONS EACH
     aa=1.d0+dfloat(iii-1)*0.4d0/2000.d0
...

これで、並列領域と の両方が、 のevolve各スレッド コピーに対してプライベートを使用しますaa。threadprivate 変数へのアクセスは通常、通常のプライベート (スタック) 変数へのアクセスよりも遅いため、64 ビット x86 システムでは、@Bálint Aradi が提案するように、代わりにの値をaa引数として渡す方が理にかなっています。evolve

于 2013-03-15T16:52:42.620 に答える
0

変数を慎重に分析する必要があります。特に、OMP プライベートとして宣言する必要があるため、異なるスレッドで同時に異なる値を持つ変数を検討する必要があります。あなたの場合、変数aaと変数の両方iiiが OMP プライベートである必要があります。変数iiiは、スレッドに分散されるループ内のカウンターであり、aaに依存する値を取得するためiiiです。

編集: 各スレッドはevolveサブルーチン自体を呼び出しevolve、スレッド固有の値 (推測) を使用することになっているため、モジュール変数を使用する代わりに、サブルーチンにaaも渡す必要があります。aaaa

ルーチンは次のようになります。

subroutine evolve(y, aa, yevl)
  integer(dp),parameter:: id=2
  real(dp),intent(in):: y(id), aa
  real(dp),intent(out):: yevl(id)
    yevl(1)=y(2)+1.d0-aa*y(1)**2
    yevl(2)=bb*y(1)
  end subroutine evolve

そして、メインプログラムでの適切な呼び出し:

call evolve(y, aa, yt)
于 2013-03-15T14:29:59.827 に答える