fortran66のブログ

fortran について書きます。

【メモ帳】CAF  縁交換

CAF (CoArray Fortran) で Halo Exchange

CAF (CoArray Fortran) で境界のヘリの交換(Halo Exchange)をやってみました。もしくは後光の交換。

Halo のイメージ なんとなくビザンチンのモザイク画
f:id:fortran66:20180823110712j:plain

Parallel Programming with Co-arrays (Chapman & Hall/CRC Computational Science)

Parallel Programming with Co-arrays (Chapman & Hall/CRC Computational Science)

10世紀末頃ビザンチンで編纂された百科全書的辞書。『スーダ』 Suda
Stoa | Welcome to the Suda On Line (SOL)

ソース・プログラム1

Intel Fortran Ver.18
コア数9個に設定 3*3 の coshape とした。上下方向には周期境界条件を、左右方向は普通の境界を設定した。各イメージ固有の配列サイズは 2*2 として、その周囲を囲むように幅 1 の縁を置いた。

以下のプログラムでは、各列ないし各行毎にシリアルな順序つき実行になっている。

    program CAF001
        implicit none
        real, allocatable :: a(:, :)[:, :]
        integer :: me, ne, nx = 2, ny = 2
        integer :: ix, iy, ipos(2)
        
        ne = num_images()
        me = this_image()
 
        allocate(a(0:nx + 1, 0:ny + 1)[3, *])
        ix = this_image(a, 1)
        iy = this_image(a, 2)
        !
        ! initial value
        a = me
        a(:, 0     ) =  0.0
        a(:, ny + 1) = 50.0
        !
        ! Halo exchange
        ! up <--> down 
        sync all
        if (ix < ucobound(a, 1)) then
            sync images ( image_index(a, [ix + 1, iy]) )
            a(0     , :)[ix + 1, iy] = a(ny, :)
        end if
        if (lcobound(a, 1) < ix) then
            a(ny + 1, :)[ix - 1, iy] = a( 1, :)
            sync images ( image_index(a, [ix - 1, iy]) )
        end if
        ! periodic boundary condition
        if (ix == ucobound(a, 1)) then 
            sync images ( image_index(a, [lcobound(a, 1), iy]) ) 
            a(0     , :)[lcobound(a, 1), iy] = a(ny, :)
        end if    
        if (ix == lcobound(a, 1)) then 
            a(ny + 1, :)[ucobound(a, 1), iy] = a( 1, :)
            sync images ( image_index(a, [ucobound(a, 1), iy]) ) 
        end if    
!
        ! left <--> right
        sync all
        if (iy < ucobound(a, 2)) then
            sync images ( image_index(a, [ix, iy + 1]) )
            a(:,      0)[ix, iy + 1] = a(:, ny)
        end if
        if (lcobound(a, 2) < iy) then
            a(:, ny + 1)[ix, iy - 1] = a(:,  1)
            sync images ( image_index(a, [ix, iy - 1]) )
        end if
        
        ! ordered output 1..ne
        if ( 1 < me) sync images (me - 1)
        print *, 'image=', me
        print '(4f5.1)', transpose(a)
        if (me < ne) sync images (me + 1)
    end program CAF001

個々の配列要素の重なりさえ避けられれば、基本的に sync はあまり必要ない。しかし要所要所で sync all することになるので、同期がモッサリする。

Modern Fortran Explained: Incorporating Fortran 2018 (Numerical Mathematics and Scientific Computation)

Modern Fortran Explained: Incorporating Fortran 2018 (Numerical Mathematics and Scientific Computation)

ソース・プログラム2

細かい sync をせず、区切りごとに sync all した場合。

どうするのが一番いいのか、よく分からない。coarray 代入には PUT 形式の方が GET 形式より、一般に性能が出やすいらしいが・・・ (PUT形式: a[i] = b, GET形式: a = b[j])

    program CAF001
        implicit none
        real, allocatable :: a(:, :)[:, :]
        integer :: me, ne, nx = 2, ny = 2
        integer :: ix, iy, ix1, ix2, iy1, iy2
        
        ne = num_images()
        me = this_image()
        print *, me, '/', ne
        
        allocate( a(0:nx + 1, 0:ny + 1)[3, *] )
        !
        ix = this_image(a, 1) 
        iy = this_image(a, 2)
        !
        ! periodic configuration
        !
        if (ix == lcobound(a, 1)) then 
            ix1 = ucobound(a, 1)
        else 
            ix1 = ix - 1
        end if    
        if (ix == ucobound(a, 1)) then 
            ix2 = lcobound(a, 1)
        else 
            ix2 = ix + 1
        end if    
        if (iy == lcobound(a, 2)) then 
            iy1 = ucobound(a, 2)
        else 
            iy1 = iy - 1
        end if    
        if (iy == ucobound(a, 2)) then 
            iy2 = lcobound(a, 2)
        else 
            iy2 = iy + 1
        end if    
        !
        ! initial value
        !
        a = me
        a(:, 0     ) =  0.0
        a(:, ny + 1) = 50.0
        !
        ! Halo exchange
        !
        ! up <--> down :  periodic boundary condition
        sync all
        a(nx + 1, :)[ix1, iy] = a( 1, :)
        a(0     , :)[ix2, iy] = a(nx, :)
        !
        ! left <--> right
        sync all
        if ( iy > lcobound(a, 2) ) a(:, ny + 1)[ix, iy1] = a(:,  1)
        if ( iy < ucobound(a, 2) ) a(:,      0)[ix, iy2] = a(:, ny)
        !
        ! ordered output 1..ne
        !
        sync all
        if ( 1 < me) sync images (me - 1)
        print *, 'image=', me
        print '(4f5.1)', transpose(a)
        if (me < ne) sync images (me + 1)
    end program CAF001

実行結果

イメージ番号配置 (処理系により勝手に設定される?)
1 4 7
2 5 8
3 6 9

上下方向には周期境界条件。左右方向は普通の境界。

 4 4 : 3 3
 4 4 : 3 3
 4 4 : 3 3
 4 4 : 3 3
 4 4 : 3 3
 4 4 : 3 3
 4 4 : 3 3
 4 4 : 3 3
 4 4 : 3 3
 image= 1
  0.0  3.0  3.0  6.0
  0.0  1.0  1.0  4.0
  0.0  1.0  1.0  4.0
  0.0  2.0  2.0  5.0
 image= 2
  0.0  1.0  1.0  4.0
  0.0  2.0  2.0  5.0
  0.0  2.0  2.0  5.0
  0.0  3.0  3.0  6.0
 image= 3
  0.0  2.0  2.0  5.0
  0.0  3.0  3.0  6.0
  0.0  3.0  3.0  6.0
  0.0  1.0  1.0  4.0
 image= 4
  3.0  6.0  6.0  9.0
  1.0  4.0  4.0  7.0
  1.0  4.0  4.0  7.0
  2.0  5.0  5.0  8.0
 image= 5
  1.0  4.0  4.0  7.0
  2.0  5.0  5.0  8.0
  2.0  5.0  5.0  8.0
  3.0  6.0  6.0  9.0
 image= 6
  2.0  5.0  5.0  8.0
  3.0  6.0  6.0  9.0
  3.0  6.0  6.0  9.0
  1.0  4.0  4.0  7.0
 image= 7
  6.0  9.0  9.0 50.0
  4.0  7.0  7.0 50.0
  4.0  7.0  7.0 50.0
  5.0  8.0  8.0 50.0
 image= 8
  4.0  7.0  7.0 50.0
  5.0  8.0  8.0 50.0
  5.0  8.0  8.0 50.0
  6.0  9.0  9.0 50.0
 image= 9
  5.0  8.0  8.0 50.0
  6.0  9.0  9.0 50.0
  6.0  9.0  9.0 50.0
  4.0  7.0  7.0 50.0