58

x86で整数レジスタをゼロ値に設定する2つのよく知られた方法があります。

また

mov reg, 0

また

xor reg, reg

値0がコードに格納されておらず、生成されたマシンコードの数バイトを節約するため、2番目のバリアントの方が優れているという意見があります。これは間違いなく良いことです-使用される命令キャッシュが少なく、これによりコードの実行が速くなる場合があります。多くのコンパイラがそのようなコードを生成します。

ただし、正式には、xor命令と、同じレジスタを変更する以前の命令との間には、命令間の依存関係があります。依存関係があるため、後者の命令は前者が完了するまで待機する必要があり、これによりプロセッサユニットの負荷が低下し、パフォーマンスが低下する可能性があります。

add reg, 17
;do something else with reg here
xor reg, reg

xorの結果は、初期レジスタ値に関係なくまったく同じになることは明らかです。しかし、プロセッサはこれを認識できますか?

VC++7で次のテストを試しました。

const int Count = 10 * 1000 * 1000 * 1000;
int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    DWORD start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            xor eax, eax
        };
    }
    DWORD diff = GetTickCount() - start;
    start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            mov eax, 0
        };
    }
    diff = GetTickCount() - start;
    return 0;
}

最適化をオフにすると、両方のループにまったく同じ時間がかかります。xor reg, regこれは、プロセッサが以前の命令に命令が依存していないことを認識していることを合理的に証明していmov eax, 0ますか?これをチェックするためのより良いテストは何でしょうか?

4

6 に答える 6

33

あなたのための実際の答え:

Intel64およびIA-32アーキテクチャ最適化リファレンスマニュアル

セクション3.5.1.8はあなたが見たいところです。

要するに、xorまたはmovが好まれる状況があります。問題は、依存関係チェーンと条件コードの保存に集中しています。

于 2009-07-16T07:31:17.303 に答える
15

最新のCPUでは、XORパターンが推奨されます。それはより小さく、そしてより速いです。

多くの実際のワークロードでは、パフォーマンスを制限する主な要因の1つがiキャッシュのミスであるため、実際には小さい方が重要です。これは、2つのオプションを比較するマイクロベンチマークではキャプチャされませんが、現実の世界では、コードの実行がわずかに速くなります。

また、i-cacheミスの減少を無視すると、過去数年間のCPUのXORは、MOVと同じかそれよりも高速です。MOV命令を実行するよりも速いものは何ですか?命令をまったく実行していません!最近のIntelプロセッサでは、ディスパッチ/名前変更ロジックがXORパターンを認識し、結果がゼロになることを「認識」し、レジスタを物理的なゼロレジスタに向けるだけです。その後、実行する必要がないため、命令を破棄します。

最終的な結果として、XORパターンは実行リソースを使用せず、最近のIntel CPUでは、サイクルごとに4つの命令を「実行」できます。MOVは、サイクルごとに3つの命令でトップアウトします。

詳細については、私が書いたこのブログ投稿を参照してください。

https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/

ほとんどのプログラマーはこれについて心配する必要はありませんが、コンパイラーの作成者は心配する必要があります。生成されているコードを理解するのは良いことです。

于 2015-03-19T23:28:26.180 に答える
13

x86には可変長命令があります。MOV EAX、0は、XOR EAX、EAXよりもコードスペースに1バイトまたは2バイト多く必要です。

于 2009-10-15T13:42:30.707 に答える
12

1966年のHRステーションワゴンを販売した後、自分の車を修理することができなくなりました。私は最近のCPUで同様の修正を行っています:-)

それは実際には基礎となるマイクロコードまたは回路に依存します。"XOR Rn,Rn"CPUが内容を気にせずにすべてのビットを認識し、単純にゼロにする可能性は十分にあります。しかしもちろん、それは。で同じことをするかもしれません"MOV Rn, 0"。優れたコンパイラはとにかくターゲットプラットフォームに最適なバリアントを選択するため、これは通常、アセンブラでコーディングしている場合にのみ問題になります。

CPUが十分に賢い場合、値が無関係であることがわかっているXORため、依存関係はなくなり、とにかくゼロに設定されます(これも、実際に使用されているCPUによって異なります)。

しかし、私は自分のコードで数バイトまたは数クロックサイクルを気にするのはずっと前のことです。これは、マイクロ最適化が狂ったようです。

于 2009-07-16T06:14:28.193 に答える
2

mov eax, 0以前のアーキテクチャでは、命令が同様に少し長くかかっていたと思いますxor eax, eax...理由を正確に思い出せません。ただし、さらに多くmovのsがない限り、コードに1つのリテラルが格納されているため、キャッシュミスが発生する可能性は低いと思います。

また、メモリから、フラグのステータスはこれらのメソッド間で同一ではないことに注意してください。ただし、これを誤って覚えている可能性があります。

于 2009-07-16T06:10:58.793 に答える
-7

コンパイラを書いていますか?

また、2つ目の注意点として、ベンチマークはおそらく機能しません。とにかく、おそらく常に時間がかかるブランチがそこにあるからです。(コンパイラがループを展開しない限り)

ループ内の単一の命令をベンチマークできないもう1つの理由は、(実際のコードとは異なり)すべてのコードがキャッシュされることです。したがって、mov eax、0とxor eax、eaxのサイズの違いの多くを、L1キャッシュに常に保持することで、画像から除外しました。

私の推測では、現実の世界で測定可能なパフォーマンスの違いは、2つのオプションの実行時間ではなく、キャッシュを使い果たしたサイズの違いによるものだと思います。

于 2009-07-16T06:48:50.073 に答える