Sun Studio 12: Fortran プログラミングガイド

10.2 自動並列化

-autopar オプションを使用すると f95 コンパイラは、効率的に並列化できる DO ループを自動的に見つけます。このようなループは変形され、利用可能なプロセッサに対してその反復が均等に分配されます。コンパイラは、このために必要なスレッド呼び出しを生成します。

10.2.1 ループの並列化

コンパイラによる依存性の解析は、DO ループを並列化可能なタスクに変形します。コンパイラは、ループの構造を変形して、逐次実行する、並列化できないセクションを切り離します。次に、利用可能なプロセッサに対して作業を均等に分配します。各プロセッサが反復の異なったブロックを実行します。

たとえば、4 つの CPU と 1,000 回の反復を持つ並列化ループの例で、各スレッドは 250 回の反復をまとめて実行します。

プロセッサ 1 が実行する反復 

から 

250 

プロセッサ 2 が実行する反復 

251 

から 

500 

プロセッサ 3 が実行する反復 

501 

から 

750 

プロセッサ 4 が実行する反復 

751 

から 

1000 

並列化できるのは、計算の実行順序に依存しないループだけです。コンパイラによる依存性の解析は、本質的にデータ依存性を持つループを拒否します。ループ中のデータフローを完全に決定できない場合、コンパイラは保守的に動作し、並列化を行いません。また、パフォーマンスの向上よりもオーバーヘッドが勝る場合、ループを並列化しないことを選択する可能性もあります。

コンパイラは常に、静的ループスケジューリング (つまり、ループ中の作業を単純に均等な反復ブロックに分割する方法) を使用して、ループを並列化することを選択することに注意してください。明示的な並列化指令を使用すれば、ほかの分配スキームも指定できます。 この指令については、この章の後半で説明します。

10.2.2 配列、スカラー、純スカラー

自動並列化という観点から、2、3 の定義が必要です。

例: 配列とスカラー


      dimension a(10)
      real m(100,10), s, u, x, z
      equivalence ( u, z )
      pointer ( px, x )
      s = 0.0
      ...

ma は両方とも配列変数です。s は純スカラーです。変数 uxzpx はスカラー変数ですが、純スカラーではありません。

10.2.3 自動並列化の基準

反復間データ依存性をもたない DO ループは、-autopar によって自動的に並列化されます。自動並列化のための一般的な基準は次のとおりです。

10.2.3.1 見かけの依存性

コンパイラは、コンパイルされたコードを変形するときに、ループ中のデータ依存の原因になりそうな (見かけの) 参照を自動的に取り除きます。このような多数の変換の 1 つは、一部の配列の専用バージョンを使用します。コンパイラがこの処理を行うことができるのは、一般的には、そのような配列が本来のループで一時領域としてのみ使用されていることが判断できる場合です。

例: -autopar を使用しています。専用配列によって依存が取り除かれます。


      parameter (n=1000)
      real a(n), b(n), c(n,n)
      do i = 1, 1000             <--並列化される
        do k = 1, n
          a(k) = b(k) + 2.0
        end do
        do j = 1, n-1
          c(i,j) = a(j+1) + 2.3
        end do
      end do
      end

前述の例では、外側のループが並列化され、別々のプロセッサ上で実行されます。配列 a を参照する内側のループはデータ依存性の原因になるように見えますが、コンパイラはその配列の一時的な専用コピーを作成して、外側のループの反復を依存しないようにしています。

10.2.3.2 自動並列化の抑制要因

自動並列化では、次のいずれかが発生すると、コンパイラはループを並列化しません。

10.2.3.3 入れ子にされたループ

マルチプロセッサシステムでは、もっとも内側のループではなく、ループの入れ子のもっとも外側のループを並列化するのがもっとも効果的です。並列処理は一般にループのオーバーヘッドがかなり大きいため、もっとも外側のループを並列化することでループのオーバーヘッドが最小になり、各プロセッサの処理量が最大になります。自動並列化では、コンパイラは入れ子のもっとも外側のループからループの解析を始め、並列化可能なループが見つかるまで、内側に進んでいきます。入れ子の中でループが 1 つでも並列化されたら、並列ループの中に含まれるループは無視されます。

10.2.4 縮約操作を使用した自動並列化

配列をスカラーに変形する計算のことを「縮約操作」と呼びます。典型的な縮約操作は、ベクトルの要素の合計や積です。 縮約操作は、ループ内の計算が反復にまたがって累積的に変数を変更しないという基準には反するものです。

例: ベクトルの要素の合計を縮訳する


      s = 0.0
      do i = 1, 1000
        s = s + v(i)
      end do
      t(k) = s

しかし、一部の操作では、並列化を妨げるのが縮約だけの場合は、この基準にかかわらず並列化できます。共通の縮約操作が頻繁に発生するので、コンパイラはこれらの操作を特別なケースであると認識し、並列化します。

-reduction コンパイラオプションが -autopar-parallel とともに指定されていなければ、縮約操作の認識は、自動並列化解析の中には含まれません。

並列化可能なループが表 10–2 にリストされた縮約操作のいずれか 1 つを持つ場合、-reduction が指定されていれば、コンパイラはそのループを並列化します。

10.2.4.1 認識される縮約操作

次の表に、f77 および f95 が認識する縮約操作をリストします。

表 10–2 認識される縮約操作

数学的な操作 

Fortran 文のテンプレート 

合計 

s = s + v(i)

積 

s = s * v(i)

ドット積 

s = s + v(i) * u(i)

最小 

s = amin( s, v(i))

最大 

s = amax( s, v(i))

OR

do i = 1, n

b = b .or. v(i)

end do

AND

b = .true.

do i = 1, n

b = b .and. v(i)

end do

ゼロでない要素の計数 

k = 0

do i = 1, n

if(v(i).ne.0) k = k + 1

end do

MIN 関数と MAX 関数はすべての形式で認識されます。

10.2.4.2 数値的な正確性と縮約操作

次の条件のため、浮動小数点の合計や積の縮約操作が不正確になることがあります。

状況によって、エラーが受け付けられない場合があります。

例: 丸めの例です。-1 と +1 の間の 100,000 個の乱数を合計します。


demo% cat t4.f
      parameter ( n = 100000 )
      double precision d_lcrans, lb / -1.0 /, s, ub / +1.0 /, v(n)
      s = d_lcrans ( v, n, lb, ub ) ! n 個の -1 と +1 の間の乱数を求める。
      s = 0.0
      do i = 1, n
        s = s + v(i)
      end do
      write(*, '(" s = ", e21.15)') s
      end
demo% f95 -O4 -autopar -reduction t4.f

結果は、プロセッサの数によって異なります。次の表に、-1 と +1 の間の 100,000 個の乱数の合計を示します。

プロセッサの数 

出力 

s = 0.568582080884714E+02

s = 0.568582080884722E+02

s = 0.568582080884721E+02

s = 0.568582080884724E+02

この状況では、丸めの誤差はおよそ 10-14 なので、この乱数のデータは容認できます。詳細は、『数値計算ガイド』を参照してください。