はじめてのSSE その4

今回はVisual C++2010で拡張命令を使う(/arch:SSE2)を指定してのコンパイルについてです。


Visual C++2010は拡張命令を使うように指定すれば*1SSE2命令が使えるようなことを書いてあるのですが、コンパイラが普通のコード生成のときに積極的に使ってくれるのかどうかについては書かれていなくてよくわかりません。


※ コメント欄で指摘をもらって気づいたのですが、x64モードならばこのオプションは指定しても意味がないようです。指定しなくともSSE2の命令を使ったコードが生成されます。



私が結論だけ書いておきます。


あまり使ってくれません。Bitboardに使っている128bitの構造体があるとして、64ビットずつxorするoperatorを書いたとします。

  void Xor1(BB bb) { m = _mm_xor_si128( m , bb.m ); }
  void Xor2(BB bb) { p[0] ^= bb.p[0]; p[1] ^= bb.p[1]; }


後者は、最適化されて前者と同等のコードが生成されたりはしません。


ゆえに、必ずand,xorなどは自力でSSE2の命令を指定してください。


では、Bitboardのコピーはどうでしょうか。


128bitコピーは、次のように_mm_loadu_si128を使ってコピーしているかも知れませんね。しかし、これがかなりの曲者です。

#define BitboardLet(b,b1)  (b).m = _mm_loadu_si128( & (b1).m )


というのも、この命令を使うことを指定するとレジスタ割り当てが阻害されることが多いからです。x86用にコンパイルしているならいざ知らず、x64用にコンパイルしている場合、この命令を指定するのは得策とは言えません。


普通にコピーするほうがまだマシ(高速)でしょう。

void operator = (const BB& b1)
{
  p[0] = b1.p[0];
  p[1] = b1.p[1];
}

では、上記のコードは最適化されて、SSE2の命令が使われないのでしょうか?


結論から言うと使われない…ようです。Visual C++ 2010の最適化が甘いと言われればそうなのですが、使われないようです。


また、_mm_loadu_si128まわりの最適化は極めて甘いのが実情です。要するに使えないのです。


ではどうすればいいのでしょうか。


私がお勧めするのは、 operator = を書かないことです。省略してしまうのです。128bitの構造体のdefault constructorは、/arch:SSE2を指定していれば(もしくはx64用のコードを生成するときは) 使えるときはSSE2の命令を使います。default constructorまわりの最適化のほうが力が入っていて良好な最適化をするということですね。


まあ、そりゃあそうでしょうね。_mm_XXXX_si128なんか使うプログラムを書いているのは一部の人のみですが、128bit構造体を使ってあるプログラムは大量にあるわけで、どちらの最適化に重きを置いてVisual C++を開発すべきかというと当然後者なのです。ゆえに、後者に力が入っているのは当然と言えるでしょう。


また、例えば/arch:SSE2を指定したからと言って5バイトの構造体をpaddingして16バイト(128bit)にするということはコンパイラはしないと思います。11バイトも勝手にpaddingされるとメモリ効率が悪くなるので、普通のプログラマはそんなことを望まないからです。


ゆえに、自分で構造体をpaddingして128bitにしておく必要があります。


まとめますと、Visual C++ 2010で構造体のコピーでSSE2の命令をうまく使うコードを生成して欲しいときは
1) x86用コードを生成するときは/arch:SSE2を指定する。
2) 構造体をpaddingして128bit(16バイト)にする。
3) コピーコンストラクタは省略する。
ということです。x64用のコードを生成するなら2),3)のみです。


■ 追記(2011/09/15)


以前書いた指し手生成ルーチンでいろいろテストしてみたところ、コピーコンストラクタは書いたほうが全体として1割程度速くなりました。コピーコンストラクタを省略する → SSE命令が使われる → SSE命令だけで完結しない処理なら汎用レジスタへの代入でメモリに退避させるコードが必要になる → SSEを使わないほうがマシということなのでしょうか…。この問題は意外と根が深そうです。




※ 私はx86用コードは書かないので、1),2),3)で本当にSSE2の命令が使われるのかどうかはよくわかりません。誰か検証を…。


つづく