SSE2を用いたRC5-64クライアントの最適化に関する考察

RC5-64とは、distributed.netが行っている並列分散処理プロジェクトのひとつです。 要するに、世界中のマシンを並列処理させて暗号を解読してしまおうというプロジェクトなんですが、 鍵が2^64=1844京6744兆737億955万1616個もあるので、マシン数万台の並列処理でもとんでもない時間がかかります。

このRC5-64のクライアントには各CPUに最適化されたコアが実装されており、CPUによってキーレートが異なります。 (キーレートの統計は、こちらに信頼性の高いデータが集まっています。) 私のMMX Pentium 200MHzのノートPCのキーレートが、約400kkeys/sです。 (MMXに最適化されていないコアに切り替えると、約280kkeys/sまで落ちます。) 一方、その10倍のクロックであるPentium 4 2GHzのキーレートは約2,800kkeys/sなので、Pentium 4のクロックあたりの処理能力はMMX Pentium以下ということになります。 (Pentiumとほぼ同等という言い方もできます。) 無論、Pentium IIを初めとするP6系CPUやAthlon,PowerPCなどよりも遅いのは言うまでもありません。 もっとPentium 4に最適な実装ができないかと、あれこれ考えてみました。

Pentium 4に実装されているSSE2命令には、MMXにそっくりの整数命令セットがあります。MMXと異なる点は、128ビットのXMMレジスタを使う点です。 他にも、64ビット整数の加減算ができることや、128ビット単位のシフトができるなどのMMXには無い特長を持っています。このSSE2命令を RC5のアルゴリズムに適用することはできないでしょうか。

RC5-64のコア部分のソースコード(nasm)を見てみると、主に次のような命令が使われています。

・32ビット整数加算(add eax, ebx) [leaを使っているところもある]
この部分に関しては、SSE2の本領を発揮できます。次の1命令で32ビットの整数4個を一度に加算できます。

paddd		xmm0, xmm1
・3ビット左ローテート(rol eax, 3)
残念ながらSSE2にはローテート命令がありません。次のようにシフト命令を応用することでローテートと同様の効果を得ることができます。

movdqa		xmm1, xmm0		;コピー
psrld		xmm0, 29			;右に29ビットシフト(32ビット単位)
pslld		xmm1, 3			;左に3ビットシフト(32ビット単位)
por		xmm0, xmm1		;全体にor
・nビット左ローテート(rol eax, n)
この部分がSSE2を使う上で最もネックとなりそうです。SSE2は各要素に対して別々の値シフトすることができないので、先ほどの方法は使えません。 方法としては、まずnに相当する値をレジスタの下位32ビットにもってきます。
xmm1 = (??,??,??,r1)
次に、ローテートさせたい値をレジスタの上位または下位の64ビットに二つ並べます。
xmm0 = (??,??,A1,A1)
最後に、64ビット単位で最初に求めた値だけ右シフトすると結果が得られます。
psllq		xmm0, xmm1		;xmm0 = (??,??,??,ROL_A1)
これを4回繰り返すことになります。かなりのクロックをロスすることが予想されます。

・排他的論理和(xor eax, ebx)
この部分も、1命令で128ビットの排他的論理和を一度に計算できます。

pxor		xmm0, xmm1

もっと良い方法がありましたらこちらまで:matsui@katto.comm.waseda.ac.jp

Last update:11/09/2001