図解

前回のエントリーで説明した書き方では大変分かりにくいので、図で示す。
Aのような配置のとき、128bitレジスタではBに示しているように白と黒をそれぞれ上位と下位64bitにして別々にした形で持っている。
この配置のうち、一番上の一行を16bit整数にする場合を取り上げる。
もちろん一番簡単な方法は0byte目と8byte目を取り出して連結することだが、それでは縦の列を計算する場合別の方法をとらなければならない。また、黒白反転は値を入れ替えればよいものの、左右対称のコードをつくる場合簡単にはいかない。(spu_reverseというような関数があればよかったのだが)
そこで汎用性があり、それほど性能的なペナルティもなさそうな方法として、Cに示した以下のような方法を採用した。

  1. spu_shuffleで、一番上の行を下位8bitに、上位8bitが0の16bit整数8つからなるベクトルを生成する(これは黒のみだが、白でも同様)
  2. spu_rlを用いて第0要素の0bit目、第1要素の2bit目…に順に対象となるマスが来るようにローテートし、spu_andで必要としないマスをマスクする
  3. 白を1bitだけローテートして、spu_orで論理和をとる
  4. spu_orxで一つの32bit整数にまとめる
  5. 32bit整数の上位16bitと下位16bitの論理和をとり、16bit整数を生成する(D)

この方法のよいところは、前回挙げたコードと、spu_shuffle, spu_rl用の二つの定数を与えるだけで、8つのマスで表現されるパターンならば全て同じクロック数で計算できることだ。このとき条件分岐、ループ、メモリアクセス、加減算を行わない。SPUのアーキテクチャではローテートがもっと早かったらとか、shortにも対応していたらとか、spu_orxがshortやlong longにも対応していたらとかいう気持ちもあるが、まあそれほどボトルネックにはなるまいという程度の性能は出ている。
実際の適用ではパターンの計算よりもパターンの評価のためのメモリアクセスの方が足を引っ張るのでここであまり力を入れても仕方がないのだが、SPUのアーキテクチャの理解という意味ではなかなか面白かった。