アトミックDMAコマンド(LL/SC)を使った同期処理を行う

出典: PS3 Linux Information Site / Cell/B.E.のパワーを体験しよう

前述のMFCアトミック更新コマンド(DMA LL/SCコマンド)を用いた複数SPEの同期処理を行います。 例としてはいささか簡単すぎるのですが、ここではある整数値を5つのSPEを使って同時にカウントアップし、PPEで最終的な整数値を表示させます。 初期値0からSPE 5個で1000回ずつカウントアップすると合計は5000になるはずですが、適切に同期処理を行なわないとうまくいきません。

画像:dma-llsc.png

ではやってみましょう。

単なるDMA転送を使った場合

DMA LL/SCコマンドを使う前に普通のDMAを使って実装しました

Media:dma_llsc_fail.tar.gz

SPEではvalue値を+1するために下記の手順でread-modefy-write操作を行います。

spu_mfcdma32 (&value,
                 value_ea,
                 sizeof(value),
                 0,
                 MFC_GET_CMD);
spu_writech(MFC_WrTagMask, 1<<0);
spu_mfcstat(MFC_TAG_UPDATE_ALL);

value += 1;
			
spu_mfcdma32 (&value,
                 value_ea,
                 sizeof(value),
                 0,
                 MFC_PUT_CMD);
spu_writech(MFC_WrTagMask, 1<<0);
spu_mfcstat(MFC_TAG_UPDATE_ALL);

ごく普通のDMA転送を行っていることに注意してください。

このプログラムを実行しすると下記の結果が得られました。

$ ./ppe.out
Sarah: setup 5 spe threads
spe: start
spe: start
Sarah: waiting all spe threads end...
spe: start
spe: start
spe: start
spe: end, count up = 1000
spe: end, count up = 1000
spe: end, count up = 1000
spe: end, count up = 1000
spe: end, count up = 1000
Sarah: close image
Sarah: delete all spe threads
value = 4068

このようにvalueは期待される値の5000ではなく4068が得られました(結果は毎回異なります)。 これはSPEで行ったvalue値の一連のread-modefy-write操作が排他的に行われておらず SPE間で競合したためと考えられます。

MFCアトミック更新コマンドを使った場合

次にSPE間で同期をとりながらカウントアップする正しい手順を提示します。MFCアトミック更新コマンド(DMA LL/SCコマンド)を使います

Media:dma_llsc.tar.gz

SPEではvalue値の+1するのに下記の手順でread-modefy-write操作を行います。

do
{
   spu_mfcdma32 (&value,
                    value_ea,
                    sizeof(value),
                    0,
                    MFC_GETLLAR_CMD);    // (1)
   if (!spu_readch (MFC_RdAtomicStat))        // (2)
       break;

   value += 1;
			
   spu_mfcdma32 (&value,
                    value_ea,
                    sizeof(value),
                    0,
                    MFC_PUTLLC_CMD);   // (3)

} while (spu_readch (MFC_RdAtomicStat));   // (4)

手順は下記の通りです。

  1. リザベーションつきでロード
  2. ステータスをチェックし成功したことを確認
  3. 条件つきストア
  4. ステータスをチェックし成功したら抜け、失敗したら再度(1)からやり直す

(1)でリザベーション付のロードを行い、(1)から(3)の間に他のSPEからvalue値に対する書き込みがあると自分が参照していたリザベーションを失い、(3)のPUTLLCコマンドが失敗します。これはvalue値に対する競合的な更新があったと考えられ、自分が参照しているvalue値は古く無効とすべきです。従って再度始めからやり直さなければなりません。一般的にLL/SCを使った操作はリトライを伴います。

実行結果を示します。

$ ./ppe.out
Sarah: setup 5 spe threads
spe: start
spe: start
spe: end, count up = 1000
spe: end, count up = 1000
Sarah: waiting all spe threads end...
spe: start
spe: start
spe: start
spe: end, count up = 1000
spe: end, count up = 1000
spe: end, count up = 1000
Sarah: close image
Sarah: delete all spe threads
value = 5000

このように今度は期待した値のvalue = 5000が得られました。



Cellプログラミングのレシピに戻る

表示
個人用ツール
Open Source Projects