はじめてのSSE その8
長々と書いてきましたが、一番基本的なことを説明するのを忘れておりました。
SSEでは、xmmレジスタという128bitのレジスタを使います。これはMMXのときのmmレジスタ(64bit)が拡張されたものです。
MMX用のコードを書いたことのある人ならご存知だと思うのですが、mmレジスタは汎用レジスタとの直交性がすこぶる悪いのです。
汎用レジスタのほうは8086の時代からax(16ビット)レジスタ → eax(32ビット) → rax(64ビット)といままで進化してきましたが、この汎用レジスタと直接演算するような命令は(たぶん)ありません。
例えば、せっかくxmmレジスタを使ってbitwise andが1命令で出来たとしても、そのあとbsrするために下位qword(64bit)を取り出すコードを書けば、xmmレジスタをいったんどこかのメモリに格納して(実際はスタックフレーム上にストアされる)、そこから汎用レジスタに取り出すコードをコンパイラは生成します。(mmレジスタが64bitでraxレジスタも64bitなのですから、直接代入できても良さそうなものですが、出来ないんですよね…。)
そんなことになるなら最初からxmmレジスタを使わないほうが断然速いんじゃないかという話もありまして、また、Visual C++2010でSSE命令(_mm_slli_si128など)を使ったコードは最適化されにくいというのもあって、x64ならこれらの命令を使わずに普通に64bitに対して演算するコードを書いて行くほうが一般的には速いプログラムになるでしょう。
つまり、x86用のコードならば、SSEを使う価値はそこそこあるのですが、x64では使いどころが非常に難しいです。私はアセンブラのコードを直接手で書いていいなら元のコードより速いコードは書けるのですが、これをVisual C++に任せた場合、たいていは元のコードより遅くなります。
SSEがその威力を発揮できるのは、大量の128bitデータに対して、SSEだけで完結できる処理を連続して繰り返すときでして、コンピューター将棋のBitboardように、128bitデータとして扱ったあと、「やっぱり下位64bitください」みたいなことには全く向いていないのです。
そういう向いてないコードを生成したとき、SSEを使うのをコンパイラが勝手にやめてくれればいいと思うのですが、コンパイラにしてみれば「ソースコードに使えって書いてあるから仕方なく使ってやってんだよ」ということでして、結局、ソースコード上でSSE命令を使うことを明示的に指定しないほうが速いコードが生成されることもしばしば。
まあ、SSE命令、部分的には使えるので、常に実行速度を計測しながら(CPUによっても特性は異なりますが)注意して使いましょうということです。
とりあえず、以上でSSE2/4についての初歩は終わりです。
次回からはSSEを用いたbsr/bsfアルゴリズム(by LS3600)について解説していきます。