fortran66のブログ

fortran について書きます。

CAF の勉強

Fortran 2008 で導入された CAF (CoArray Fortran) 関係の命令を少し勉強したので、まとめをメモしておきます。coarray は PGAS 言語に分類される並列プログラミング用の仕組みです。ここで扱うのは Fortran 2008 の範囲で、Fortran 2015 で拡張される予定の部分については扱いません。勉強中なので間違っていたらゴメン!


コードを適切にセグメントに分けると、その部分集合 {Pi} はシリアル実行なら全順序集合としての構造を持つ。

CAF による並列実行の時、各イメージが独立も実行され相互にやり取りをしないなら、それぞれが全順序集合のコードセグメント {Pi}, {Qi}, {Ri}・・・ のように分かれる。各イメージごとに、CoArray を通じたデータのやり取りをする場合、データの読み込み・書き込みは排他的に順序づけて行なわなければならない。つまりコードセグメント間に順序関係を考えねばならない。

異なるイメージのコードセグメント間に順序関係を考えるとき、コードセグメント全体としては、半順序の構造をもつ。CAF でのデータのやり取りは、順序が定義されているコードセグメント間でしか許されない。(半順序なので、任意の二つのコードセグメント (Pi, Qj) を取り出したとき、この二つに順序が定義されているとは限らない。

CAF でコードセグメント間に順序を定義するときは、sync 命令を用いる。

一つのコードセグメントが複数のコードセグメントとデータをやり取りする時、データをやり取りする二つのコードセグメント間に順序関係が定義されても、データを直接やり取りしないコードセグメント間には順序が定義されていない。この場合、これらの間の順序は任意であるので、順序に依らずにデータ読み書きの排他性を保つ必要がある。この目的で critical..end critical、lock..unlock、atomic の三つの命令がある。

critical と lock はコード実行に関する排他性を保証する機能であり、atomic はデータへのアクセスの排他性を保証する機能である。critical は全イメージがブロッキング型で排他実行される。lock はより詳細に排他実行されるイメージを制御でき、ノンブロッキング型の排他実行も可能である。atomic 命令は、変数へのアクセスを排他的に行うための命令群であり、ノンブロッキング型の排他実行になっている。


d.hatena.ne.jp

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

プログラム例

独立なイメージの実行 (イメージ間の順序構造なし)

ソース・プログラム
    program CAF00
      implicit none
      print *, 'Hello from', this_image(), 'out of', num_images(), 'images.' 
    end program CAF00
実行結果
 Hello from           3 out of           8 images.
 Hello from           6 out of           8 images.
 Hello from           8 out of           8 images.
 Hello from           1 out of           8 images.
 Hello from           7 out of           8 images.
 Hello from           2 out of           8 images.
 Hello from           5 out of           8 images.
 Hello from           4 out of           8 images.
続行するには何かキーを押してください . . .

イメージの実行 (環状の順序構造: 切れ目が必要)

ソース・プログラム
    program CAF04
      implicit none
      integer :: m[*], itmp[*]
      integer :: i, n, k
      n = num_images()
      i = this_image()
      m = i
      itmp = m
      print *, i, m
      sync all
      
      k = i - 1
      if (k == 0) k = n
      m[k] = itmp

      if (i == 1) print *
      sync all
      print *, i, m
    end program CAF04
実行結果
 3 3
 1 1
 2 2
 4 4

 3 4
 1 2
 2 3
 4 1
続行するには何かキーを押してください . . .

順序を完全な円環にすると、身動きが取れなくなるので、切れ目を入れて直線と端点処理にしている。
f:id:fortran66:20170603215639p:plain

Critical による排他実行

ソース・プログラム
    program CAF05
      implicit none
      integer :: me, ni
      integer :: k[*] = 1
      integer :: p(4)[*] 
      
      me = this_image()
      ni = num_images()
      sync all

      critical
        p(k[1])[1] = me
        k[1] = k[1] + 1
      end critical    
      
      sync all
      if (me == 1) print *, p
    end program CAF05
実行結果
           1           3           4           2
続行するには何かキーを押してください . . .

lock による排他実行

ソース・プログラム
    program CAF08
      use, intrinsic :: iso_fortran_env, only : lock_type
      implicit none
      type(lock_type) :: list_lock[*]  
      integer :: i, p[*] = 0
      p = this_image()
      sync all
      if (this_image() == 1) then
        do i = 2, num_images()
           lock(list_lock[1])
             p[1] = p[1] + p[i]
           unlock(list_lock[1])
        end do
        print *, this_image(), num_images(), p
      end if
    end program CAF08    
実行結果
           1          10          55
続行するには何かキーを押してください . . .

atomic による変数への排他アクセス

ソース・プログラム
  program CAF10
    use, intrinsic :: iso_fortran_env
    implicit none
    logical(atomic_logical_kind) :: locked[*] = .true.
    logical :: val
    integer :: me, p = 1, q = 2, i = 0
    me = this_image()
    if (me == p) then
      sync memory
      call atomic_define(locked[q],.false.)
    else if (me == q) then
      val = .true.
      do while (val)
        i = i + 1  
        call atomic_ref(val,locked)
      end do
      sync memory
    end if
    print *, me , i
  end program CAF10    
実行結果
           1           0
           2         188
続行するには何かキーを押してください . . .

イメージ1が初めに lock に排他的にアクセスしているあいだ、イメージ2は実行可能になるまで、188回ループを回って順番を待っている。(ノンブロッキング実行のため待たされることは無い)