SSE についてのメモ(2) SSE4など

VisualStudio2005 は /arch:SSE2 オプションで SSE 命令を使って
コンパイルしてくれます。とはいえ積極的なベクトル化までは行われないし
SSE3/SSSE3 を含めて全部の SSE 機能が使われるわけではありません。
これらの命令を使う手段として、
・アセンブラ
・インラインアセンブラ
・コンパイラの intrinsic 命令
があります。

MSDN MMX, SSE, and SSE2 Intrinsics

コンパイラ命令として intrinsic を使うためには、/Oi オプションが必要で
それぞれ専用のヘッダファイルを include します。

・MMX mmintrin.h
・SSE xmmintrin.h
・SSE2 emmintrin.h

ネットで調べると SSE3/SSSE3 命令を使う場合はさらに

・SSE3 pmmintrin.h
・SSSE3 tmmintrin.h

が必要になるとのこと。でも VisualStudio2005 (VC8) の include には
これらのファイルが見つかりません。
さらにもう少し調べてみたところ、VC8 では intrin.h を include するだけで
SSE~SSE3 の命令が使えるようになるそうです。

例えば SSE3 で追加された水平加算命令 haddps は、intrinsic では
_mm_hadd_ps() となります。実際にコンパイルして実行できました。

SSE は基本的に垂直演算用に設計されているため、水平加算などの命令は
ほとんど用意されていませんでした。

確かに Matrix など常に複数のベクトルがセットで用いられる場合は
垂直演算だけで4個いっぺんの積和で求まるのでそれほど困りませんでした。
でも単発で出てくる細かい積和や内積では、dp3/4 一発で済む shader と
違って結構命令数がかかってしまいます。_mm_hadd_ps() はこんなときに
重宝しそうです。

水平加算とはいえ2要素単位で合計を求める命令なので、4要素全部の加算には
2命令分の実行になります。内積の場合

__m128 va= _mm_load_ps( &_va );
__m128 vb= _mm_load_ps( &_vb );
va= _mm_mul_ps( va, vb );
va= _mm_hadd_ps( va, va );
va= _mm_hadd_ps( va, va );

と命令数がかなり減ります。アセンブラで比較すると

; SSE
movps xmm0, XMMWORD PTR _va
movps xmm1, XMMWORD PTR _vb
mulps xmm1, xmm0
movps xmm0, xmm1
shufps xmm0, xmm1, 01001110b
addps xmm0, xmm1
movps xmm1, xmm0
shufps xmm1, xmm0, 10110001b
addps xmm1, xmm0

; SSE3
movps xmm0, XMMWORD PTR _va
movps xmm1, XMMWORD PTR _vb
mulps xmm0, xmm1
haddps xmm0, xmm0
haddps xmm0, xmm0

演算部の 7命令が 3命令になっています。
非常に便利になった気がしますが、でもこれも SSE4 が出るまでの話。
もうすぐ登場予定の SSE4 ではそのものずばり DotProduct 命令 dpps が
追加されるので、上記の演算3命令すら1つで済んでしまいます。

Intel SSE4 Programming Reference

こちらの SSE4 命令の詳細マニュアルによると、dpps は単に内積の積和を
演算するだけでなく、入力値と出力値のマスクも可能になっています。
Shader の swizzle ほど入力の自由度はないですが、それでも 3×3 内積を
簡単に指定できます。
今までわざとプログラマに意地悪しているんじゃないかと思えるほど
使いづらい仕様だった SSE 命令が、SSE4 では急にフレンドリーになったように
感じます。突然命令が複雑化したのでレイテンシは増えそうです。
実際に登場したらどれくらいの速度になるか、どれだけスループットを稼げるか
みものでしょう。

他にも SSE4 で便利そうな命令として extractps, insertps があります。

extractps は、128bit 中任意のスカラー(32bit 値)の取出しです。
例えば shader でいう src.y , src.w といった単一要素の参照を簡単にします。
これまで無かったのが不思議です。
Opcode は 6byte ほど。さすがに長くなっています。

insertps はさらに 128bit 中任意の 32bit に値を書き込みもできます。
一度に 1要素のアクセスに制限されますが、任意のスカラー値のコピーが
1命令でできるようになりました。

dest.z = src.y とかできるわけですね。

このあたりのドキュメントを読んでいたせいか、今朝

 ものすごい簡単で便利な命令が追加されたけど、
 プリフィックスだらけで命令長が 20byte 以上、
 めちゃめちゃ長くなっている。

・・という夢を見ました。
未来の x86 拡張命令はいったい何 byte になってるんでしょうか。

SSE についてのメモ(1)