fortran66のブログ

fortran について書きます。

配列代入のLOOP構造分類

Fortranは配列操作が得意なわけですが、配列に要素を計算して代入するときに、その要素が配列のインデックスにどの程度依存するかで並列化に適したLOOP構造を選べます。

  1. 配列の要素が全くインデックスによらない場合、インデックスを省略した記法が使えます。(例:b = a)
  2. 配列の要素が、その要素のインデックスのみに依るのならば、各要素は独立なので、順序を気にせず並列に計算できます。この場合は、FORALLないしDO CONCURRENTが使えます。FORALLは基本的に代入操作にしか使えず、かつ設計仕様が複雑すぎて実装が難しく、並列化も効かせにくいので、使わないほうがよいとされていますが、1行でLOOP構造が書けるという便利な点もあります。(例:FORALL(i=1:n) a(i) = i**2)DO CONCURRENTはこの欠点を踏まえてFORALLを代替するものとしてFortran2008で導入された命令です。代入操作以外にも使え、並列化にも有利ですが、1行でLOOPを書くことはできません。
  3. 最後に配列の要素が、漸化式のように自己のインデックス以外の配列要素にアクセスしている場合は、計算の順序に依存性があるので、基本的にシリアルに計算する必要があります。この場合は従来のDO LOOPを使用することになります。

配列要素に何らかの条件を課す時、前二者ではMASKを利用できます。普通のDO LOOPではIF文を用いて記述することになります。

  1. WHERE (a>0.0) b = LOG(a)
  2. FORALL (i=1:n, a>0.0) b(i) = a(i) * log(real(i)) あるいは DO CONCURRENT (i=1:n, a>0.0); b(i) = a(i) * log(real(i)); END DO
  3. DO i = 1, n ; if (a(i) > 0.0) b(i) = b(i - 1) + LOG(a(i)); END DO

これらとELEMENTALな副プログラムを組み合わせることにより、抽象度が高くコンパイラが並列化しやすいプログラムを書くことが可能になります。

ソースプログラム

program LOOP
  implicit none
  integer, parameter :: n = 5
  real :: a(n), b(n), c(n), d(n)
  integer :: i
  a = [(real(i - (n + 1) / 2),   i = 1, n)]
! no dependence on index
  b = a**2
! local dependence on index   i
  forall(i = 1:n) c(i) = i * a(i)
! local dependence on index   ii
  do concurrent (i = 1:n)
    c(i) = i * a(i)   
  end do
! non-local dependence on index
  d(1) = 0.0
  do i = 2, n
   d(i) = d(i - 1) + a(i)
  end do
  print *, a
  print *
  print *
  print *, b
  print *
  print *, c
  print *
  print *, d
  print *
!
! masking 
!
  b = 0.0
  c = 0.0
  d = 0.0
! no dependence on index
  where(a >= 0.0) b = a**2
! local dependence on index    i
  forall(i = 1:n,  a(i) >= 0.0) c(i) = i * a(i)
! local dependence on index    ii
  do concurrent (i = 1:n,  a(i) >= 0.0)
    c(i) = i * a(i)   
  end do
! non-local dependence on index
  d(1) = 0.0
  do i = 2, n
    if (a(i) >= 0.0) d(i) = d(i - 1) + a(i)
  end do
  print *
  print *, b
  print *
  print *, c
  print *
  print *, d
  print *
!
!  masking with else
!  
  b = 0.0
  c = 0.0
  d = 0.0
! no dependence on index
  where(a >= 0.0) 
    b = a**2
  else where
    b = 0.0 / 0.0 ! NaN
  end where
! local dependence on index   i
  forall(i = 1:n, a(i) >= 0.0) c(i) = i * a(i)
  forall(i = 1:n, a(i) <  0.0) c(i) = 0.0 / 0.0
! local dependence on index   ii
  do concurrent (i = 1:n) 
    if (a(i) >= 0.0) then
      c(i) = i * a(i)
    else
      c(i) = 0.0 / 0.0 ! NaN
    end if  
  end do
! non-local dependence on index
  d(1) = 0.0
  do i = 2, n
    if (a(i) >= 0.0) then
      d(i) = d(i - 1) + a(i)
    else
      d(i) = a(i)
    end if
  end do
  print *
  print *, a
  print *
  print *, b
  print *
  print *, c
  print *
  print *, d
  print *
!  
  stop
end program LOOP

実行結果