NNI

命令セットの話である。NehalemにはSSE4.2が搭載されるという話は既にレポートした通りであるが、IDFではこのSSE4.2の拡張と、これに続くものとしてIntel AVXの話題が出てきた。ただIntel AVXのターゲットはNehalemの次世代(Westmereの更に次)なので、こちらは別にレポートすることにして、とりあえずNNIのみ紹介したい。

さてNNIことSSE4.2(Photo43)である。Intelとしては珍しく、SSE4.2に関してはPenrynのSSE4.1と一緒に命令の詳細が公開されており、SSE4.1は47命令、SSE4.2は7命令となっている。このSSE4.2で追加された命令の大半は全くSIMDとは無関係な処理で、このためかIntelはSTTNI/ATAという呼び方をしている。大半、というのはSTTNIの4命令、ATAの2命令は全くSIMDではなく、唯一PCMPGTQ(Compare Packed Data for Greater Than)だけがSIMDらしい命令と言えるが、実はこれは64bitの比較演算である。一応上位64bitと下位64bitを別々に比較し、結果も別々に返ってくるのでSIMDといえばSIMDだが、ちょっと毛色が異なっている。

Photo43:最近はこうした言い方をあまりしなくなったが、Katmaiで追加されたSSEがKNI(Katmai New Instruction)、Prescottで追加されたSSE3がPNI(Prescott New Instruction)といった具合。ただConroeのSSSE3とかPenrynのSSE4.1では、こうした呼ばれ方はしないあたりが不思議だ。

さて残りの6つだが、STTNIとATAは全く異なる命令である(Photo44)。STTNIには、

・PCMPESTRI - Packed compare explict-length strings, return index in ECX/RCX
・PCMPESTRM - Packed compare explict-length strings, return mask in XMM0
・PCMPISTRI - Packed compare implict-length strings, return index in ECX/RCX
・PCMPISTRM - Packed compare implict-length strings, return mask in XMM0

という4つの命令が含まれる。

これを使って何ができるか、というのがPhoto45である。STTNI命令はSIMDではないが、XMMレジスタを使って高速な文字列処理を行うというコンセプトである。具体的な例がPhoto46に示されているが、これは直接アプリケーションプログラマが扱うというよりも、文字列ライブラリの中をこれで作り変えるといった趣の命令である。実際にstrlen()(文字列の長さを返す関数)を記述した例が示されているが(Photo47)、どちらかというとこれはIntelがPerformance Primitiveとしてライブラリを提供すべき命令の類な気がする。

Photo44:判りやすく説明するためにはこの方が良いのだろうが、無視されたPCMPGTQがちょっと可哀想である。

Photo45:当然ながら、一度に比較できる文字列の長さは最大16Bytes(=128bit)に限られる。もっとも、もっと大きな文字列を比較するといっても、結局内部的には1Byteずつループさせながら比較するというのが従来の実装だから、まとめて16Bytes分比較できれば高速になるのは当然とは言える。これでXMLのパースが最大3.8倍高速になり、命令Cycle数を約3分の1にできるとしており、この命令のスループットは案外大きいのかもしれない。

Photo46:左上は、2つの文字列を比較し、1つでも一致した文字があればその場所を返す"Equal Any"、右上は2つの文字列を順に比較し、一致している/いないを返す"Equal Each"、左下は与えられた文字列が範囲(この例ならA-Zまたは0-9)に入っているかを比較して結果を返す"Range"、右下は2つの文字列をシフトさせながら、どこから一致し始めるかを返す"Equal ordered"となる。

Photo47:右は16Bytesずつポインタをずらしながらmovdquで渡された文字列をXMM1に格納し、それと"\0"が入ったバッファ(XMM2)をPCMPISTRIで比較し、最初に一致した場所を返すという実装。確かに簡単ではある。

次にATAのほうだが(Photo48)、CRC32はいわゆる32bitのCRCを計算するもの、POPCNTはBitMaskの比較(与えられた値の中でbitに"1"がセットされている数を返す)というものだ。POPCNTは、これまでだとbit演算を多用することになり、x86そのままだとどうしても時間がかかる内容だから判らなくもないし、CRC32は特に通信などでは多用されるから命令を入れても悪くは無いだろう。CRC32の場合の性能向上例がPhoto49だが、これはやはりサーバー向けの拡張と考えたほうが良いだろう。これらの命令は、Intel C++の10.xやVisual Studio 2008などでサポートされるとしている(Photo50)。

Photo48:もっとも、どちらも余りコンシューマ向けには関係ないという気もする。実際CRC32の例はiSCSIを使うケースだし、POPCNTはパターンマッチング一般とはいえ、これが使える例が凄く多い訳ではない。

Photo49:左はGCCのインラインアセンブラでCRC32の演算を高速化したサンプルの模様。データ量が多いと最大18倍以上高速化される、という事だそうだが……。

Photo50:64bitではVisual C++でインラインアセンブラが使えないため、intrinsicsを使うしか選択肢はなさそうだ。いやならIntel C++を使えという事なのだろうが。GCCとかでどうなるか、はまだ不明。一応IPP(Intel Performance Primitive)でもこれらの新命令がサポートされる見込みはありそうだ。