三元运算符vs if语句:编译器优化

时间:2013-05-22 16:06:30

标签: c++ c optimization ternary-operator

这是:

int val;  
// ...
val = (val != 0) ? otherVal : 0;

效率低于此:

int val;
//...
if (val != 0)
    val = otherVal;

编译器是否能够优化三元运算符?意图很明确,有没有什么方法可以实际写0到内存?也许当内存映射到文件?

我们可以假设没关系吗?

编辑:关键是如果满足一个条件,则将变量设置为某个值。没有其他想要的分支。这就是为什么我问三元(带有强制性的其他分支应该制作副本)是否会降低效率或优化。

5 个答案:

答案 0 :(得分:4)

您的编译器将对其进行优化。最后,表现几乎没有差别。

然而,可读性存在很大差异。有时,三元运算符可以帮助删除许多代码,这些代码不会在清晰度上添加很多。

在其他情况下,if声明更清晰,更容易理解。

将代码缩减为三元语句,但为了保持清晰度而必须添加大量注释会适得其反。

所有编码之神,请不要嵌套三元语句。

答案 1 :(得分:4)

Mats Petersson的建议通常是最好的“编写最易读的变体”。 但是,如果您正在尝试编写最佳速度性能代码,则需要了解有关计算机和处理器的更多信息。对于某些机器,第一台将运行得更快(高度流水线处理器:没有分支,优化的三元运算符)。其他机器将以第二种形式运行得更快(更简单)。

答案 2 :(得分:3)

您可以使用无分支三元运算符,有时也称为bitselect(条件?true:false)。

不要担心额外的操作,与if语句分支相比,它们无关紧要。

bitselect implementation:

inline static int bitselect(int condition, int truereturnvalue, int falsereturnvalue)
{
    return (truereturnvalue & -condition) | (falsereturnvalue & ~(-condition)); //a when TRUE and b when FALSE
}

inline static float bitselect(int condition, float truereturnvalue, float falsereturnvalue)
{
    //Reinterpret floats. Would work because it's just a bit select, no matter the actual value
    int& at = reinterpret_cast<int&>(truereturnvalue);
    int& af = reinterpret_cast<int&>(falsereturnvalue);
    int res = (at & -condition) | (af & ~(-condition)); //a when TRUE and b when FALSE
    return  reinterpret_cast<float&>(res);
}

答案 3 :(得分:2)

这主要是Ternary operator ?: vs if...else

的副本

对于大多数编译器而言,效率将是相同的,编译器将优化三元运算符,就像它优化if / else语句一样。也就是说,我更喜欢if语句,因为它们使代码更容易阅读。

回答您的其他问题。我不确定你的意思,如果你只是将一个整数或变量设置为0,那么除了像上面那样将它设置为零之外没有更快的方法。

如果你有一个变量数组,你可以使用memset(ptr, 0, size*sizeof(TYPE)),如果你想要设置为零的变量数组,这可能是最快的。或者也许是std :: fill_n

我不确定你用上述逻辑尝试实现的目标,但似乎有点奇怪。有一些方法可以安排代码,这意味着你可能根本不需要条件,但是如果没有看到更多的代码,很难说出你的情况。

说实话,除非你做了数十亿次这样的操作,否则这可能是非常成熟的优化,你应该专注于可读性。

答案 4 :(得分:0)

诸如“编译器资源管理器”之类的工具非常擅长回答此类问题。修复了代码和comparing the following two snippets中的错误后,我们看到它们在-O1及更高版本上产生相同的汇编。

void trinary(int& val, int otherVal) {
    val = (val != 0) ? otherVal : 0;
}

void nontrinary(int& val, int otherVal) {
    if(val != 0) {
        val = otherVal;
    }
    else {
        val = 0;
    }
}

trinary(int&, int):
        mov     eax, DWORD PTR [rdi]
        test    eax, eax
        mov     eax, 0
        cmove   esi, eax
        mov     DWORD PTR [rdi], esi
        ret
nontrinary(int&, int):
        mov     eax, DWORD PTR [rdi]
        test    eax, eax
        mov     eax, 0
        cmove   esi, eax
        mov     DWORD PTR [rdi], esi
        ret

有趣的是,它们在-O0不会产生相同的输出。在-O0,编译器使用eax显式存储三进制运算符的结果,然后在返回之前将eax复制到正确的寄存器中。非三重版本直接执行分配。

trinary(int&, int):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     DWORD PTR [rbp-12], esi
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        test    eax, eax
        je      .L2
        mov     eax, DWORD PTR [rbp-12]
        jmp     .L3
.L2:
        mov     eax, 0
.L3:
        mov     rdx, QWORD PTR [rbp-8]
        mov     DWORD PTR [rdx], eax
        nop
        pop     rbp
        ret
nontrinary(int&, int):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     DWORD PTR [rbp-12], esi
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        test    eax, eax
        je      .L5
        mov     rax, QWORD PTR [rbp-8]
        mov     edx, DWORD PTR [rbp-12]
        mov     DWORD PTR [rax], edx
        jmp     .L7
.L5:
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 0
.L7:
        nop
        pop     rbp
        ret