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

问题描述 投票:6回答:5

是这个:

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

效率低于此:

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

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

我们可以认为这没关系吗?

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

c++ c optimization ternary-operator
5个回答
4
投票

Mats Petersson的建议通常是最好的“写出最具可读性的变体”。但是,if试图编写最佳速度性能代码,则需要了解有关计算机和处理器的更多信息。对于某些机器,第一个会运行得更快(高度流水线处理器:无分支,优化的三元运算符)。使用第二种形式(更简单),其他计算机将更快地运行。


4
投票

您的编译器将对其进行优化。最后,性能几乎没有差异。

但是,可读性有很大差异。有时,三元运算符可以帮助删除很多不太清晰的代码行。

在其他情况下,if语句更清晰,更易于遵循。

将代码简化为三元语句,但随后为了保持清晰性而不得不添加大量注释会适得其反。

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


3
投票

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

不用担心额外的操作,与if语句分支相比,它们什么都不是。

位选择实现:

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);
}

2
投票

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

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

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

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

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

老实说,除非您进行数十亿次此操作,否则这可能是非常过早的优化,您应该专注于可读性。


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
© www.soinside.com 2019 - 2024. All rights reserved.