テクニカルノート

ベクトル計算高速化についての考察(1997/5/8)

← 戻る

Alphaプロセッサには、毎クロックごとにデータを投入し結果を出力できる浮動小数点の加算、乗算パイプラインが備えられ、クロックの2倍のピーク性能(500MHz CPUで1GFLOPS)を持っています。本ノートではDEC FORTRANを用いてAlphaプロセッサの性能を引き出すプログラミングテクニックを紹介します。

1.Alpha21164の構造と性能

Alpha21164及び21164Aには浮動小数点演算用に各々7段のステージを持つ乗算、加算パイプラインが用意されています。両パイプラインはデータ投入後7クロック後に計算結果を出力することができます。従って、理想的に両パイプラインにデータを投入でき、結果を格納できればクロックの2倍即ち500MHz CPUなら1GFLOPSのピーク性能を実現できます。しかしながら、ある数の乗算を行い、その結果を用いて次の乗算を行なう様なシーケンシャルなプログラムの場合、1GMFLOPSのピーク性能にも拘わらず、500/7=71MFLOPSの性能しか実現できません。

2.高速プログラミングテクニック

[ベースとなったddot  プログラム1]
      double precision function ddot(v1, v2, len) len=256
      integer len, i
      double precision v1(len), v2(len)
      ddot = 0.0d0

      do i = 1, len

       ddot = ddot + v1(i) * v2(i)

      enddo
      end

[改良したddot     プログラム2]
      double precision function ddot(v1, v2, len) len=256
      integer len, i
      double precision v1(len),v2(len),t1,t2,t3,t4
      t1 = 0.0d0
      t2 = 0.0d0
      t3 = 0.0d0
      t4 = 0.0d0
      do i = 1, len, 32
 式?   t1 = t1+v1(i+00)*v2(i+00)+v1(i+01)*v2(i+01)+
     Q        v1(i+02)*v2(i+02)+v1(i+03)*v2(i+03)+
     Q        v1(i+04)*v2(i+04)+v1(i+05)*v2(i+05)+
     Q        v1(i+06)*v2(i+06)+v1(i+07)*v2(i+07)
 式?   t2 = t2+v1(i+08)*v2(i+08)+v1(i+09)*v2(i+09)+
     Q        v1(i+10)*v2(i+10)+v1(i+11)*v2(i+11)+
     Q        v1(i+12)*v2(i+12)+v1(i+13)*v2(i+13)+
     Q        v1(i+14)*v2(i+14)+v1(i+15)*v2(i+15)
 式?   t3 = t3+v1(i+16)*v2(i+16)+v1(i+17)*v2(i+17)+
     Q        v1(i+18)*v2(i+18)+v1(i+19)*v2(i+19)+
     Q        v1(i+20)*v2(i+20)+v1(i+21)*v2(i+21)+
     Q        v1(i+22)*v2(i+22)+v1(i+23)*v2(i+23)
 式?   t4 = t4+v1(i+24)*v2(i+24)+v1(i+25)*v2(i+25)+
     Q        v1(i+26)*v2(i+26)+v1(i+27)*v2(i+27)+
     Q        v1(i+28)*v2(i+28)+v1(i+29)*v2(i+29)+
     Q        v1(i+30)*v2(i+30)+v1(i+31)*v2(i+31)

      enddo
      ddot = t1+t2+t3+t4
      end

プログラム1では、i番目項の乗算を行なって結果をddotに加算し、その後i+1番目の計算を行なう様にプログラムされています。従って、このままコードが生成されると

 乗算器へのデータ投入  7clock   結果の取得  v1(i)*v2(i)
 加算器へのデータ投入  7clock   結果の取得  ddotに加算

となり、1回のループの実行に14クロックかかりそうですが、DEC FORTRANでは以下に説明する様に、加算器と乗算器を並列に実行するコードを生成してくれます。

DEC FORTRANはループの展開オプションを付けずにコンパイルするとループを展開してコンパイルするので、基本的な性能を計るためループを展開せず(/unroll:1)にコンパイルすると、実行時間は4.65マイクロ秒(110.1MFLOPS)でした。1ループ当り4.65/256=0.018マイクロ秒即ち0.018*500=9クロックで実行したことになります。加算器からの結果を待たずに次の乗算を開始し、加算と乗算を並列に実行したと推測できます。しかし、乗算器へのデータ投入後7クロック待っているのは確かです。

 次に、ループの展開をデフォルトでコンパイルすると(/unroll:4だと思われる)実行時間は2.59マイクロ秒(197.4MFLOPS)、/unroll:16では2.27 マ イ ク ロ秒(225.2MFLOPS)でしたので、ループの展開によるオプティマイズが有効になっているのがわかります。

 改良されたプログラム2では、加算器、乗算器の間の並列だけではなく、7クロック待っている間に次のデータを投入しパイプラインをフルに稼動させようと試みています。このプログラムをループ展開せずにコンパイルしたい場合、実行時間は1.52マイクロ秒(336.1MFLOPS)に短縮され、平均して3サイクルごと加算と乗算を行ないました。式?から?は相互に関連しませんので、式?のデータがパイプラインに投入された後、その結果を待たずに式?のデータが投入されるという様に、四つの式が並列に実行されたのです。/unroll:4、/unroll:16でもほぼ同様の結果となりました。

 以上に述べた様に、DEC FORTRANはCPUの性能を良く引き出すことができるコンパイラであると言えるでしょう。高速化を図りたい時には、/unrollオプションにトライするのも有効でしょう。更に高速化を図りたい時には、プログラム2の様に相互に影響しない式を羅列し、パイプラインの効率を上げることも一つの有効な手段です。