« Cell vs Pentium4 - スカラー演算編 | トップページ | Cell vs Pentium4 - SIMD演算編(その2) »

Cell vs Pentium4 - SIMD演算編

前回に引き続き、行列演算のサンプルプログラムを使ったCellと Pentium4の性能比較を行います。今回は以下を目標にサンプルプログラムを改造しました。

  • SIMD (Single Instruction Multiple Data)演算を使用して処理性能を上げる
  • Cellでは複数のSPEに処理を分割して並列動作させることでさらに性能を上げる

SIMD演算では、以下のように複数の要素(単精度浮動小数点の場合4要素)に対して単一の命令で計算を行うことができます。例えば、以下の足し算を1つの命令で実行できます。
  c[0] = a[0] + b[0]
  c[1] = a[1] + b[1]
  c[2] = a[2] + b[2]
  c[3] = a[3] + b[3]

SPEのSIMD演算では、上記の計算を以下のSIMD命令で置き換えることが可能です。
  c = spu_add(a, b)

これまでなら、ループを4回まわして計算したところを、1回の計算でできてしまうわけですから行列計算の高速化にはうってつけです。前回の性能比較に使用した1024 x 512の行列の足し算プログラムをCellとPentium4用に作成しました。今回使用したソースコードを以下に示します。
 Cell用ソース「matrix-add-simd.zip」をダウンロード
 P4用ソース「matrix-add.sse.zip」をダウンロード
Pentium4は単純に行列の足し算部分をSIMD(SSE)命令に置き換えただけですが、Cellの場合はちょっとやっかいです。

SIMD演算を使用したいだけなら、PPEのAltiVec(PowerPCのSIMD演算機能)を使用する手もありますが、今回はSPEへの演算のオフロードと並列化が目的です。そのためには、SPEに処理を分割したり、PPEとSPEはメモリーを共有していないので計算用のデーターをSPEにDMA転送したりする必用があります。PLAYSTATION Linux完全攻略ガイドに掲載されているサンプルプログラムを参考にCell版のプログラムを作成しました。Cell版プログラムの概要は以下となります。

Matirxaddsimd

プログラムの実行結果は以下となりました。

Cell vs Pentium4 -SIMD演算
項目Cell(gcc)-1SPECell(gcc)-2SPECell(xlc)-1SPECell(xlc)-2SPPP4(gcc)
CPU Cell BE 3.2GHz P4 530 3.18GHz
コンパイラ ppu-gcc 4.1.1
spu-gcc 4.1.1
IBM xlc 0.8.2 gcc 4.1.1
最適化レベル O3 O3 O3 O3 O3
計算時間(s) 31.15 30.80 13.06 12.78 6.64
18.74

例によってppu-gccでコンパイルしたコードの実行結果は遅いのですが、XLCの実行結果をもってしてもP4に勝てませんはPentium4を上回りました。だた、SPUx1, SPUx2で実行時間に殆ど差がありません(表には記載していませんが、SPUx4では僅かに速度が低下してしまいます)。

(5/17追記) Pentium4 SSE版にバグがありました。行列の初期化が本来必要な量の1/4しかできていませんでした。どおりで早いはずです。バグ修正版の測定データーに上記の表を更新しました。Pentium4はスカラー演算版でも18.81sで計算を行っていたので、SSEを使用してもほとんど高速化しないということになります。この結果はちょっと謎ですが、Pentium4の場合、単精度浮動小数点の足し算レベルではFPUとSSEで性能差がないということになります。

今回のプログラムは、行列の初期値を生成するために以下のコードをPPEで実行しています。

void init_mat()
{
    int i, j;

    for (i = 0; i < NUM_ROW; i++)
    {
        for (j = 0; j < NUM_COL; j++)
        {
            mat_a[i][j] = (float)(i * NUM_COL + j);
            mat_b[i][j] = (float)((i * NUM_COL + j)*2);
            mat_c[i][j] = 0.0f;      
        }
    }
}

即ち、上記のコードで生成した行列から、mat_c[i, j] = mat_a[i, j] + mat_b[i, j]の足し算をSPUで実行します。この処理を1000回ループして実行時間を測定しているため、PPEでの初期値生成を1000回行っていることになるのですが、PPE側の初期値計算量がオーバーヘッドになっていると思われます。同様にメインメモリ - SPE間のDMAもオーバーヘッドになっている可能性があります。

Cell SDK 2.1に入っているCell SimulatorにSPEの稼働状況をグラフ化する機能があるため、この機能を使用してSPEの動作状況を調べてみました。結果は以下となります。

グラフより、時系列的に見ると、以下の順番でSPEが稼働していることが分かります。

 ①SPE0とSPE1 → ②SPE6とSPE7 → ③ SPE4とSPE5 → ④SPE2とSPE3

2つのSPEは同時に起動されている(即ち平行して動いている)と思われます。ただし、毎回異なるSPEが起動される理由は謎です(PS3用Cellのsimulatorではないため、8個のSPEが存在するのは仕様だと思います)。

Sim1_4

グラフにあるように、SPEが起動されてから次の起動までの間が、PPEにて行列の初期化を行っている時間と思われます。この時間を短縮するためにはPPEで初期値データーを生成しながらSPEにデーターを供給するように、PPEとSPEを並列動作させる必用がありそうです(もしくは、初期値生成もSPEにオフロードするか)。初期値の生成と計算を並列動作させるには大幅なプログラム修正がが必用なので今回はここまでであきらめました。

PS3のCellはSPEの計算性能を単純に足し算すると、ピーク性能で218GFLOPSをマークするとされていますが、DMAのオーバーヘッドや今回のように計算の元ネタデーター生成能力を考えると、Cellの演算性能を引き出すのは骨が折れそうです。いわゆる、Cellの性能を引き出すプログラミングは難しいというやつですね。

(5/17追記)Pentium4版プログラムのバグを修正した結果、Cell用コンパイラの最適化にすがると、現時点でもPentium4を越えることができました。Cell版プログラムの性能ネックはPPEで行っている行列の初期化にあることは分かっており、こちらにAltiVecを使用すると劇的に性能が向上することも分かっています。Pentium4は今回の結果から、行列の初期化部分をSSE化しても性能向上はないことが予想され、その場合はCellの完全勝利が可能となります。Cellの性能向上版を使用した比較は次回掲載したいと思います。

今回のプログラムではまった点

Cのプログラムを書いたのが久々だったので、ポインタや型変換などCのプログラムではまるポイントは今回一通りはまったのですが、以下の点が私的には盲点でした。

1. 構造体のパディング

Cコンパイラは、構造体をデーターアクセスの効率がよいワード境界にアライメントするために、構造体メンバーにパッドを付与します(パッドの付与のしかたは、ターゲットとするCPUのアーキテクチャーによって異なります)。そのため、構造体メンバーのバイト長を足し算した結果と、sizeof(構造体名)の値が異なる場合があることが分かりました:
例えば以下の構造体のデーターサイズは、CellやP4では28byteでなく32byteになります。すなわち、Cellではデーターが8byte境界にアライメントされるようです。
Pentium4の場合、データーサイズは28となり、4byte境界にアライメントされることが分かります。
 -- 2007/5/14 P4のアライメント条件を修正 --

typedef struct {
    unsigned long long    ea_a;   // 8 byte
    unsigned long long    ea_b;   // 8 byte
    unsigned long long    ea_c;   // 8 byte
    unsigned int size;  // 4 byte
} DMA_params_t;

2. DMAバスエラーのsimulation

CellのDMAバイト数は16バイトの倍数にする必用があるのですが、上記の構造体に誤ったパディングを設定してデータ長が40byteになったためDMA転送が失敗していました。実機で動かすとDMAのコードで「バスエラー」と表示されプログラムが異常終了するのですぐ分かるのですが、PC Linux上で動作するEclipse + simulatorでは異常終了が発生しないので問題に気がつくのに時間を要しました。EclipseのデバッグwindowにDMA statusの表示があるのでここを注視すれば分かったのだと思いますが、simulatorを過信してはいかんという事例ですね。

3. Cell SDK gccのデフォルコンパイルモード

Cellは32bitおよび64bitのコードを実行できます。YDLやFCに入っているgccはデフォルトで32bitのコードを生成しますが、SDK 2.1のppu-gccはデフォルトで64bitのコードを生成します。そのため、longのデーター長が、YDL/FCのgccでは32bit、SDKのppu-gccでは64bitになります。今回のプログラムでは問題になりませんでしたが、long変数のデーター長に依存したプログラムを書く場合は注意が必要です(コンパイラオプションに明示的に-m32をつけて32bitコードを指定する必用があります)。

« Cell vs Pentium4 - スカラー演算編 | トップページ | Cell vs Pentium4 - SIMD演算編(その2) »

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

コメント

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

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      
無料ブログはココログ