ということで、何となく続きものになってしまいました・・・。
今回はSIMD命令に関連した注意点を見てみましょう。
SIMDを組み込み関数を使って書くときの注意点
32bitのXMMレジスタは8個だが、64bitのXMMレジスタは16個ある
通常、レジスタ数なんていうのはコンパイラが何とかしてくれます。
ところが、SIMD命令を書くときはかなり別になります。特にSIMD命令では速度が重要になるものなので、
速度のペナルティとなるメモリのアクセスを最低限に抑える(特に書き込みを最小限にする)のが重要です。
そのため、32bit版で組んでいると無理矢理レジスタを使い回すためにずいぶん無茶なコードを書くときがあります。
ところが、64bit版ではレジスタが16個あるので、それに対応したコードを書いてもよい、というものがあります。
基本的には演算メモリを8つで使い回すようにコードを考えた方がいい(32bitと64bitの互換のため)ですが、64bit専用で組むのであれば、この考え方を使うべきだと思います。
ま、組み込み命令ならレジスタ数を無視して書いてもコンパイラの最適化で何とかなる場合もありますが・・・。
SSE2の整数演算の速度は使い方によってはMMXの演算速度の1/2に
これが意外な間違いの原因です。SSE2の最適化で起こる現象として以下のようなものがあります。
「SSE2の整数演算はMMXの演算器を2回動かす(64bitx2=128bit)ことで行われる」
つまり、単純にMMXと同じように64bit幅の演算を行うとMMXに比べてSSE2は速度が見かけ上1/2になるという困った現象が起こります。(それでも使わないよりは早いですが)
このため、SSE2以上で整数演算を行うときはできる限り128bitをすべて使うように変形したルーチンを使う必要があります。
その2で示したルーチンは64bit幅のままでやっていたのである意味誤りになります。
ちなみに、SSE2がMMXに対してアドバンテージがある演算は
- メモリのコピーが128bit幅で行える(メモリのキャッシュ制御もできる)
- unpack処理(interleave処理)は命令あたりの速度がMMXの命令あたりの速度と同じ(同じビット幅で見ると2倍速)
- shuffle処理をつかってword幅やdword幅のデータをレジスタにコピーできる
- 除算処理が必要になったときにSSEを使って整数=>浮動小数=>逆数の浮動小数=>逆数の整数ということができる(整数には除算処理がない。SSEでも除算処理はかなり遅いので注意)
というところです。これを使わないとSSE2はMMXに速度的に敗北することになります。
これを知らないと、「SSE2を使っているはずなのにMMXより遅い。新しい命令を使っているはずなのに・・・」という現象が起こります。というか過去に自分が体験しました。
2010.01.22 追記:Pentium4の時代はこのような現象が多々存在しましたが、Core MicroArchitectureになってからはだいぶ改善されているようです。
この間違いを修正するためにルーチンの改良をやっているわけですが・・・。間抜けですね。