fortran66のブログ

fortran について書きます。

CAF の critical,lock,atom の違い

機能の違いをつらつら読んで見ますと、以下のような違いがあります。

critical no flag blocking random order
lock lock_type blocking random order
lock+acquired lock_type+logical non-blocking random order
atom atomic_logical_kind/atomic_int_kind non-blocking user-specified order

critical

criticalは最もお手軽で、余分な変数がいりません。全imageが対象になります。ひとつの image だけが critical 領域を実行でき、残りの image はブロックされて、lock が順々に解除されるまで待つことになります。

program caf_critical
      implicit none
      integer :: m[*] = 0
      
      critical 
        m[1] = m[1] + 1
      end critical 
      sync all
      print *, this_image(), m

      stop
end program caf_critical

lock

lock は lock変数が必要になります。intrinsic な定義型 type(lock_type) 形が lock となります。critical との違いは、lock を image ごとに指定できるので、特定の image 群に関しての処理ができる点だと思います。(critical は全 image 一括での扱い。)

    program CAF_lock
      use iso_fortran_env
      implicit none
      integer :: m[*] = 0, imags
      type(lock_type) :: flag[*]
      
      imags = mod(this_image(), 2) + 1 ! images 1 or 2
      lock(flag[imags])
        m[imags] = m[imags] + 1
      unlock(flag[imags])
      sync all
      print *, this_image(), m
      
      stop
    end program CAF_lock

偶数番 image と奇数番 image で集計変数を変えています。

lock+acquired_lock

lock で acuired_lock オプションをつけると、lock を取れなかった image はブロックされて待つことなく実行を続けます。したがって、lock 解放待ちの処理もユーザー側で行わなければなりません。

    program CAF_lock_acquire
      use iso_fortran_env
      implicit none
      integer :: m[*] = 0
      type(lock_type) :: flag[*]
      logical :: acloc

      do 
        lock(flag[3], acquired_lock = acloc)
        print *, this_image(), acloc
        if (acloc) exit
      end do
      m[3] = m[3] + 1
      unlock(flag[3])
      sync all
      print *, this_image(), m

      stop
    end program CAF_lock_acquire

atom

atom は一番面倒で、Modern Fortran Explained 非使用奨励になっています。
一番素朴な機能を持っており、lock にあたる操作を行うとき、どの image を lock するかというところからユーザーが決めないとなりません。逆に言うと、どの image の順で実行を行うかユーザー側が指定できます。

    program caf_atom
      use iso_fortran_env
      implicit none
      integer :: m[*] = 0
      logical :: val
      logical(atomic_logical_kind) :: atom[*] = .true. 
      integer :: i

      do i = 1, num_images()
        if (this_image() == i) then
          m[4] = m[4] + 1
          call atomic_define(atom[4], .false.)
          print *, this_image(), 'atomic'
          sync memory
        else   
          val = .true.
          do while(val)
            call atomic_ref(val, atom[4])
          end do
          sync memory
        end if  
      end do
      sync all
      print *, this_image(), m
      
      stop
    end program caf_atom