布尔技巧上的比较算子 在C++中,逻辑运算符和< /代码>,代码> > ,!代码>存在,分别对应于连接、析取和否定

布尔技巧上的比较算子 在C++中,逻辑运算符和< /代码>,代码> > ,!代码>存在,分别对应于连接、析取和否定,c++,boolean,comparison-operators,boolean-operations,C++,Boolean,Comparison Operators,Boolean Operations,但是我注意到比较运算符==,=,,=也可以用于布尔值!假设P和Q是布尔值: p==Q是双内容的 p!=Q是独占析取 pQ是非隐含的 p=Q是相反的含义 我的问题是: 通过这个技巧,程序的性能会更好吗 是否有使用此技巧的示例代码(任何语言) 这个技巧会提高性能吗 在任何有好处的处理器上,当p和Q是简单变量时,这将非常简单,您应该期望编译器已经使用它,而不需要任何源代码重写 但是请记住,p比较运算符超过逻辑运算符的缺点,我已经发布了一个优化建议,关于ISO C++提案。祝你们好运,但老实说,我不期望

但是我注意到比较运算符
==
=
=
也可以用于布尔值!假设
P
Q
是布尔值:

p==Q
是双内容的

p!=Q
是独占析取

p
是逆非蕴涵

p>Q
是非隐含的

p=Q
是相反的含义

我的问题是:

  • 通过这个技巧,程序的性能会更好吗

  • 是否有使用此技巧的示例代码(任何语言)


  • 这个技巧会提高性能吗

    在任何有好处的处理器上,当
    p
    Q
    是简单变量时,这将非常简单,您应该期望编译器已经使用它,而不需要任何源代码重写

    但是请记住,
    p
    通常比
    有明显的缺点!P&Q
    :如果
    P
    的计算结果为
    true
    ,则当结果已知时,需要对
    Q
    进行计算。这同样适用于所有其他关系运算符

    是否有使用此技巧的示例代码(任何语言)

    这不是一个技巧,而是因为它可以带来更容易理解的代码(不是任何特定的语言):


    它可以用
    ^
    编写。哪个更容易阅读是一个意见问题。

    事实上,我认为这可能会使代码更快。以下是第一个函数的代码:

    bool biconditional(bool a, bool b)
    {
        return (a && b) || (!a && !b);
    }
    
    bool biconditional_trick(bool a, bool b)
    {
        return a == b;
    }
    
    以及生成的程序集:

    biconditional(bool, bool):
            mov     eax, esi
            xor     eax, 1
            xor     eax, edi
            ret
    biconditional_trick(bool, bool):
            cmp     dil, sil
            sete    al
            ret
    
    biconditional_with_function():
            sub     rsp, 8
            call    fa()
            test    al, al
            je      .L7
            call    fb()
            test    al, al
            jne     .L10
    .L7:
            call    fa()
            mov     edx, eax
            xor     eax, eax
            test    dl, dl
            je      .L14
    .L10:
            add     rsp, 8
            ret
    .L14:
            call    fb()
            add     rsp, 8
            xor     eax, 1
            ret
    biconditional_with_function_trick():
            push    rbx
            call    fa()
            mov     ebx, eax
            call    fb()
            cmp     bl, al
            pop     rbx
            sete    al
            ret
    
    biconditional_with_function_rewritten():
            push    rbx
            call    fa()
            mov     ebx, eax
            call    fb()
            xor     eax, 1
            xor     eax, ebx
            pop     rbx
            ret
    
    我使用了来自的gcc 5.3,带有标志
    -O3-Wall-fno verbose asm-march=haswell

    很明显,你可以删掉一条指令。有趣的是,gcc没有进行这种优化。您可能想给他们发封电子邮件,问他们为什么:

    编辑:另一个答案很好地说明了一点:通过修剪不必要的部分,可以更快地计算逻辑表达式。为了演示,我重写了代码以使用对返回
    bool
    的函数的调用,而不是
    bool
    参数:

    bool fa();
    bool fb();
    
    bool biconditional_with_function()
    {
        return (fa() && fb()) || (!fa() && !fb());
    }
    
    bool biconditional_with_function_trick()
    {
        return fa() == fb();
    }
    
    以下是大会:

    biconditional(bool, bool):
            mov     eax, esi
            xor     eax, 1
            xor     eax, edi
            ret
    biconditional_trick(bool, bool):
            cmp     dil, sil
            sete    al
            ret
    
    biconditional_with_function():
            sub     rsp, 8
            call    fa()
            test    al, al
            je      .L7
            call    fb()
            test    al, al
            jne     .L10
    .L7:
            call    fa()
            mov     edx, eax
            xor     eax, eax
            test    dl, dl
            je      .L14
    .L10:
            add     rsp, 8
            ret
    .L14:
            call    fb()
            add     rsp, 8
            xor     eax, 1
            ret
    biconditional_with_function_trick():
            push    rbx
            call    fa()
            mov     ebx, eax
            call    fb()
            cmp     bl, al
            pop     rbx
            sete    al
            ret
    
    biconditional_with_function_rewritten():
            push    rbx
            call    fa()
            mov     ebx, eax
            call    fb()
            xor     eax, 1
            xor     eax, ebx
            pop     rbx
            ret
    
    您可以看到,如果表达式的前半部分为true,则为
    biconditional_with_function
    生成的代码使用跳转跳过表达式的后半部分。有趣的是,在计算后半部分时,
    fa()
    fb()
    总共被调用两次,因为编译器不知道它们是否总是返回相同的结果。如果是这种情况,则应通过将评估结果保存在其自身变量中来重写代码:

    bool biconditional_with_function_rewritten()
    {
        bool a = fa();
        bool b = fb();
        return (a && b) || (!a && !b);
    }
    
    大会:

    biconditional(bool, bool):
            mov     eax, esi
            xor     eax, 1
            xor     eax, edi
            ret
    biconditional_trick(bool, bool):
            cmp     dil, sil
            sete    al
            ret
    
    biconditional_with_function():
            sub     rsp, 8
            call    fa()
            test    al, al
            je      .L7
            call    fb()
            test    al, al
            jne     .L10
    .L7:
            call    fa()
            mov     edx, eax
            xor     eax, eax
            test    dl, dl
            je      .L14
    .L10:
            add     rsp, 8
            ret
    .L14:
            call    fb()
            add     rsp, 8
            xor     eax, 1
            ret
    biconditional_with_function_trick():
            push    rbx
            call    fa()
            mov     ebx, eax
            call    fb()
            cmp     bl, al
            pop     rbx
            sete    al
            ret
    
    biconditional_with_function_rewritten():
            push    rbx
            call    fa()
            mov     ebx, eax
            call    fb()
            xor     eax, 1
            xor     eax, ebx
            pop     rbx
            ret
    
    我们可以看到它们几乎完全相同,只剩下1指令的差异,这给了“技巧”方法一点优势


    对于相反的非蕴涵,我们可以看到,当使用逻辑运算符时,GCC确实会避免计算第二个运算符,但当
    强制转换是什么时,则不会?为什么不
    P
    P>Q
    P=Q
    ?哦,我不知道,=也可以用来表示布尔值。谢谢。“这个技巧会提高性能吗?”比什么提高?速度。这不是很明显吗?我的意思是与只使用
    &&
    |
    的代码相比 >比较运算符超过逻辑运算符的缺点,我已经发布了一个优化建议,关于ISO C++提案。祝你们好运,但老实说,我不期望从中发生很多事情:您建议对语言规则添加一个特殊的例外,该规则可能改变现有代码的行为,不要给出任何令人信服的用例。你认为
    P
    有什么好处!P&Q
    ?嗯,真的。对于第一个示例,有一个旧的For-GCC,尽管现在生成的代码已经比以前好得多。这并不像“指令越少,速度越快”那么简单,因此可能值得检查两个版本的实际指令成本,但除非它们的速度完全相同,否则其中一个版本的优化可能会遗漏。