kmraの日記: 【バイナリアンネタ】 sbb命令はもはや速くない?
Binary hackで使われる命令にsbb (キャリー付き減算)があります。
この命令を使うことで分岐命令を排除して高速に実行させるというコーディングテクニックがあったのですが、マイクロアーキテクチャの変化によりこういうテクニックが使えなくなってしまっていたようです。
元々sbbは何に使うかというと多倍長の演算を行うときに使用します。
例えば64ビットの演算をする際にx86では32ビットまでの演算しか定義されていないので
sub eax, ebx
sbb ecx, edx
とすることで{ecx, eax}-{edx, ebx}の64ビットの引き算をします。
ですが、バイナリアンのsbbの使い方はちょっと違います。
どう使うかというと
unsigned int x, y; // signed intでは動かないので注意!
if (x < y) {
return -1;
} else {
return 0;
}
というコードがあったとき
sub x,y ; x - y, x < yならcy=1
sbb eax, eax ; eax = eax - eax - cy
; eax - eax = 0なので
; cy=1ならeax=-1, cy=0ならeax=0
ret ; eaxがreturnの戻り値
と書くのです。このコードでは遅い条件分岐が不要になるので高速なのです。
他にも、cyの0/1にif文の条件を反映させてからsbb eax, eaxやmov eax, 1; sbb eax, 0とするなどいろいろ便利に使えます。
ですが、Pentium世代になってcmovやsetcc命令が定義されてからは使われることが少なくなっていたのでしょう。
Intelの発行するIA-32命令のレイテンシとスループットにはsbb reg,regのレイテンシは8、スループットは3とあります。
これに対して通常のadd/subであればレイテンシは0.5、スループットは0.5ですからsbbは遅い命令の部類でしょう。
レイテンシはフラグ設定に時間が掛かるためかもしれませんが、setccでもレイテンシ5、スループット1.5とのことなので、意図的に遅くていい命令に分類されているのでしょう。
フラグの結果の適用先はほぼ条件分岐命令のみ、その条件分岐命令は分岐予測で高速化することが前提なので、sbbなどのフラグを参照する演算命令はx86の大きなプロセッサでは遅くなる運命なのかもしれません。
【バイナリアンネタ】 sbb命令はもはや速くない? More ログイン