1

C で 2 つの値の間で変数を切り替える 3 つの方法を試しました。コードは次のとおりです。

http://pastebin.com/K481DsU3

void with_xor(int *value)
{
    *value ^= VAL_ONE ^ VAL_TWO;
}

void with_conditional(int *value)
{
    *value = (*value == VAL_ONE)? VAL_TWO : VAL_ONE;
}

void with_if(int *value)
{
    if(VAL_ONE == *value)
    {      
        *value = VAL_TWO;
    }
    else
    {
        *value = VAL_ONE;
    }
}

「xor」アプローチは他の 2 つのアプローチよりもはるかに高速であると予想していましたが、そうではないようです。何故ですか?

テスト結果は次のとおりです。

xor           0.052300          
conditional   0.035738          
if            0.034924          

これは逆アセンブリです(フラグなしでコンパイルされています):

_with_xor:
0000000100000bd0    pushq   %rbp
0000000100000bd1    movq    %rsp,%rbp
0000000100000bd4    movq    %rdi,0xf8(%rbp)
0000000100000bd8    movq    0xf8(%rbp),%rax
0000000100000bdc    movl    (%rax),%eax
0000000100000bde    xorl    $0x0f,%eax
0000000100000be1    movq    0xf8(%rbp),%rcx
0000000100000be5    movl    %eax,(%rcx)
0000000100000be7    popq    %rbp
0000000100000be8    ret
0000000100000be9    nopl    0x00000000(%rax)
_with_conditional:
0000000100000bf0    pushq   %rbp
0000000100000bf1    movq    %rsp,%rbp
0000000100000bf4    movq    %rdi,0xf8(%rbp)
0000000100000bf8    movq    0xf8(%rbp),%rax
0000000100000bfc    movl    (%rax),%eax
0000000100000bfe    cmpl    $0x0a,%eax
0000000100000c01    jne 0x100000c0c
0000000100000c03    movl    $0x00000005,0xf4(%rbp)
0000000100000c0a    jmp 0x100000c13
0000000100000c0c    movl    $0x0000000a,0xf4(%rbp)
0000000100000c13    movq    0xf8(%rbp),%rax
0000000100000c17    movl    0xf4(%rbp),%ecx
0000000100000c1a    movl    %ecx,(%rax)
0000000100000c1c    popq    %rbp
0000000100000c1d    ret
0000000100000c1e    nop
_with_if:
0000000100000c20    pushq   %rbp
0000000100000c21    movq    %rsp,%rbp
0000000100000c24    movq    %rdi,0xf8(%rbp)
0000000100000c28    movq    0xf8(%rbp),%rax
0000000100000c2c    movl    (%rax),%eax
0000000100000c2e    cmpl    $0x0a,%eax
0000000100000c31    jne 0x100000c3f
0000000100000c33    movq    0xf8(%rbp),%rax
0000000100000c37    movl    $0x00000005,(%rax)
0000000100000c3d    jmp 0x100000c49
0000000100000c3f    movq    0xf8(%rbp),%rax
0000000100000c43    movl    $0x0000000a,(%rax)
0000000100000c49    popq    %rbp
0000000100000c4a    ret
0000000100000c4b    nopl    0x00(%rax,%rax)
4

2 に答える 2

3

まず第一に、コードの「条件付き」と「if」のバリアントは機能的に同一であり、実際、コンパイラは両方に対してほぼ同一のコードを生成しています。

ここでは、分岐予測が結果に大きく影響する可能性があります。常に 5 と 10 の間で値を切り替えてまっすぐ戻るため、条件付き/if バリアントの分岐はほぼ 100% の精度で予測でき、関数を比較とほぼ同時に実行できるストア。一方、XOR バリアントは、load-xor-store シーケンスとして実行する必要があります。これら 3 つの操作はいずれも、前の操作が完了するまで実行できません。

于 2012-07-02T00:49:34.710 に答える
0

通常、CPU で xor をパイプライン処理することはできませんが、move 命令をパイプライン処理することはできます。そのため、XOR プロセスは非常に遅くなります。

基本的に、命令パイプラインでは、できるだけ多くの命令を実行する必要があります。一般に、分岐予測は非常に優れているため、CPU はこれらすべての mov 命令を正しく解決し、すべてが並列で終了する場所を決定できます。一方、先行するものが XOR される前に XOR することは不可能であるため、CPU は次の XOR 命令を実行する前に、前の XOR 命令が解決されるのを待つ必要があります。はるかに遅い。

于 2012-07-02T00:44:15.273 に答える