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 命令は、変数へのアクセスを排他的に行うための命令群であり、ノンブロッキング型の排他実行になっている。
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 続行するには何かキーを押してください . . .
順序を完全な円環にすると、身動きが取れなくなるので、切れ目を入れて直線と端点処理にしている。
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回ループを回って順番を待っている。(ノンブロッキング実行のため待たされることは無い)