« Cell vs Pentium4 - SIMD演算編 | トップページ | Cellプログラミング-DMAの高速化 »

Cell vs Pentium4 - SIMD演算編(その2)

前回に続いて行列計算プログラムのチューニングを行いました。

まずは、Pentium4版ですが、以下のように行列の初期化部分をSSE命令に置き換えました。ソース全体はこちらです。

void init_mat_sse ()
{
  int i;
  __m128  *a = (__m128 *) mat_a;
  __m128  *b = (__m128 *) mat_b;
  __m128  *c = (__m128 *) mat_c;
  __m128  offset = {4.0, 4.0, 4.0, 4.0};

  a[0] = _mm_setr_ps(0.0, 1.0, 2.0, 3.0);
  b[0] = _mm_add_ps(a[0], a[0]);
  c[0] = _mm_setzero_ps();
 
  for (i = 1; i < NUM_ROW*NUM_COL/4; i++)
  {
    a[i] = _mm_add_ps(a[i-1], offset);
    b[i] = _mm_add_ps(a[i], a[i]);
    c[i] =  _mm_setzero_ps();
  }
}

mat_a[i][j]は、以下のように数字を1づつ増やしているだけです。
  mat_a[0][0] = 0.0
  mat_a[0][1] = 1.0
  mat_a[0][2] = 2.0
  mat_a[0][3] = 3.0
  mat_a[0][4] = 4.0
       ・
       ・
  mat_a[0][511] = 511.0
  mat_a[1][0] = 512.0

SSE (SIMD)命令を使用した行列の初期化ですが、まず初期値として、単精度小数x4のベクター型ポインター aを宣言して、行列データーの格納位置であるmat_aの先頭アドレスを代入します。これで、a[0]を指定することで、mat_a[0][0]〜mat_a[0][3]の4つの要素にアクセスすることができます。

先ず、a[0] = _mm_setr_ps(0.0, 1.0, 2.0, 3.0) 命令で、mat_a[0][0]〜mat_a[0][3]に{0.0, 1.0, 2.0, 3.0}の初期値を代入します。
次に、a[1]はmat_a[0][4]〜mat_a[0][7]に対応し {4.0, 5.0, 6.0, 7.0}の値を取りますが、この値を作るためにはa[0]の各要素に4.0を足せばよいことになります。この計算は、a[i] = _mm_add_ps(a[i-1], offset) 命令で実現できます。

mat_bはmat_aの2倍の値を代入するだけです。b = a x 2でもよいのですが、b = a + aと足し算にした方が処理が早いかもと思い、b[i] = _mm_add_ps(a[i], a[i]) としています(たぶん、掛け算命令を使用しても差はないです)。

このプログラムを実行すると、なんと、計算時間が18.74秒から 6.67秒に短縮できてしまいました。現時点でのCellの計算時間は12.78秒のため、2倍以上の高速化が必要です。前回行列の足し算部分をSSE化した際は全く高速化できなかったのに対して、今回は大幅な高速化ができました。メモリーストアー命令を多用する初期化処理はSSEによるSIMD化が有効な結果となりました。

Cell版のプログラムでも、PPEで行っていた行列の初期化を同様にAltiVec (PowerPCのSIMD命令)に書き換えるつもりでしたが、この程度ではPentium4の性能に届きそうにありません。Pentium4恐るべしです。対抗策として、これまではPPEで行列の初期値を作成してSPEで足し算を実行していたのですが、初期値作成と足し算の両方をSPEで行うようにします。従って、プログラムのフローは以下となります。

Matirxaddsimd2

Cell版プログラムのソースはこちらから

SPEで行っている行列の初期化と足し算部分は以下の通りです。

vector float *vec_a  = (vector float *) mat_a_LS;  // SPE LS内のbuffer
vector float *vec_b  = (vector float *) mat_b_LS;
vector float *vec_c  = (vector float *) mat_c_LS;
vector float offset = (vector float) {4.0, 4.0, 4.0, 4.0};
vector float zero = (vector float) {0.0, 0.0, 0.0, 0.0};

for (i = 0; i < SPU_BUFFER/16; i++) {   // ループ回数の計算は手抜き
     // Initialize Matrix
     vec_a[i] = spu_add(a, offset);
     vec_b[i] = spu_add(vec_a[i], vec_a[i]);
     vec_c[i] = zero;            // P4版との性能比較上あえて初期化する

     // Compute
     vec_c[i] = spu_add(vec_a[i], vec_b[i]);
     a = vec_a[i];
}

さて性能はどうでしょうか。過去のデーターもあわせてまとめてみました。

Cell vs Pentium4 -SIMD演算
項目P4(半SIMD)P4(全SIMD)Cell(半SIMD)
2 SPE
Cell(全SIMD)
1 SPE
Cell(全SIMD)
2 SPE
CPU P4 530 3.18GHz Cell BE 3.2GHz
コンパイラ gcc 4.1.1 IBM xlc 0.8.2
最適化レベル O3 O3 O3 O3 O3
計算時間(s) 18.74 6.67 12.78 1.19 1.03
  • P4半SIMD: 行列計算部分のみSIMD化
  • P4全SIMD: 行列初期値生成・計算の両方をSIMD化
  • Cell半SIMD: 行列計算部分のみSPEでSIMD化
  • Cell全SIMD: 行列初期値生成・計算の両方をSPEでSIMD化

今回SPEでの完全SIMD化によって、P4の性能に完全勝利できました。
スペース関係で掲載しませんが、Cell用のgccを使用した場合でも2秒以下で計算が可能であり、Cell用コンパイラの最適化効果で勝ったとも言わせません。時間がかかりましたが、なんとか当初の目的を達成です。

考察

Cellはスカラー性能は平凡ですが、やはりSPEを駆使したSIMD(ベクトル)演算性能には目をみはるものがあることが分かりました。今回の性能測定からGFLOPS性能を計算すると以下となります。

GFLOPS性能
項目P4(全SIMD)Cell(全SIMD)
1 SPE
Cell(全SIMD)
2 SPE
GFLOPS 0.31 1.76 2.03

GFLOPSの計算方法は以下です。

  • 行列の初期化で3回・足し算で1回、合計要素あたり4回の浮動小数点演算を行うと考える(初期化の1処理は単なる代入ですが、計算の範疇に加えます)
  • 要素数は1024 x 512
  • ループの繰り返し回数が 1000回

即ち合計計算回数 = 4 x 1024 x 512 x 1000 = 2,097,152,000  となります
この計算回数を実行時間で割った値がGFLOPS値です。

上記の実測値に対してSPEの論理性能は、spu_add命令がクロックサイクル毎に実行できるとした場合以下の数値になると思います。

  • 一回の演算毎に、1)演算器へのデーターのロード、2)計算、3)メモリーへの計算結果のストアーと、3サイクルが必用。ロードに関しては、レジスタ内の値を使用できるるため2サイクルとする
  • SIMDによって、2サイクルで単精度浮動小数点の4要素が計算できる
  • SPEのクロックサイクルは3.2GHzである

SPEの性能 = 4 * 3.2 / 2 = 6.4GFLOPS

2007/5/24追記:
上記のGFLOPS計算に関して、一つ気がつきました。SPEも内部でパイプライン処理を行っているはずなので、ロード・計算・ストアをパイプライン処理できかつLS(Local Store)とのロード・ストアが1クロックサイクルで実行できればSPEの論理性能は4*3.2 = 12.8GFLOPSになります。

実際の演算では、計算結果のDMA転送やループ処理のオーバーヘッドがあるため、今回の性能値である1.76GFLPSはそこそこの数値だと思われます。今回のプログラムは、計算結果をメインメモリーにDMA転送するする処理の比率が大きく、メインメモリーの帯域ネックによって性能が上がらないものと思われます。それでもPS3はメインメモリーにXDRを奢っておりメモリー帯域が25.6GB/sあるため、My PCのDDRメモリー6.4GB/sに比べると4倍の帯域を持っており、この分が性能差に現れたと思われます。

演算性能に関しては、Pentium4に完勝しましたが、SPEの論理性能は25.6GFLOPSとも言われており、この値と実測値がに大きな乖離があります。論理性能は恐らく、以下の計算から求めているのだと思います。

  • 積和のように、1クロックで2演算できる命令を考える
  • SIMDでは、1クロックに4要素処理できるため、クロックあたり8回演算したことと等価になる
  • SPEのクロックが3.2GHzのため、GFLOPS = 8 x 3.2 = 25.6GFLOPS

上記の計算は、演算データーのロードや演算結果のストアを無視しており、強いて言えばレジスタ内の演算をループを使わずに繰り返した際のピーク性能となります。従って、実際の計算においてこの論理性能が発揮できることはありえないということになります。

2007/5/24追記:
こちらについても、ロード・計算・ストアをパイプライン処理で1クロックサイクル毎に実行できれば論理的には可能ですね。そのため、データーがLS内に全て格納でき、メモリー間で転送が不要であるようなデーターの局所性が極めて高いプログラムならピーク性能近くを出せるかもしれません。いつか試してみたいです。

ただ、Cellに限ったことではなく、Intelを含めて全てのSIMDプロセッサの演算性能は上記のようなロード・ストアを無視したピーク性能で計算していると思われます。従って、実性能はさておき、比較の上では意味があるのだと思います。

Pentium4の論理性能は、3GHzで12GFLOPSとされていますが、論理性能 vs 実性能比でもCellがPentium4を上回ったことになります。

« Cell vs Pentium4 - SIMD演算編 | トップページ | Cellプログラミング-DMAの高速化 »

Cellプログラミング」カテゴリの記事

コメント

P4用コードですが最適化によって70%ほど高速化できるようです。(Core2 Duo 2GHz gprofで計測)
Core2 DuoではPentium4よりSSE命令が高速に実行できます。これにデュアルコアによるマルチスレッド化があれば
結構、いい勝負になるのではないのでしょうか。

blackswordさんコメントありがとうございました。私が持っているP4(Prescott)ではSSE命令の演算機が1つなのでHT(マルチスレッド)の恩恵はあまりないと考えていました。Core2 Duoならその点マルチコアの高速化が期待できますね。

この記事へのコメントは終了しました。

« Cell vs Pentium4 - SIMD演算編 | トップページ | Cellプログラミング-DMAの高速化 »

2018年10月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ