C++ 三元运算符vs if语句:编译器优化
这是:C++ 三元运算符vs if语句:编译器优化,c++,c,optimization,ternary-operator,C++,C,Optimization,Ternary Operator,这是: int val; // ... val = (val != 0) ? otherVal : 0; 效率低于此: int val; //... if (val != 0) val = otherVal; ? 编译器是否能够优化三元运算符?意图很清楚,是否有任何方法可以将0写入内存?也许当内存映射到文件时 我们能假设这不重要吗 编辑:要点是在满足一个条件的情况下将变量设置为某个值。没有其他的分支。这就是为什么我问一个三元结构(带有必须复制的else分支)是否效率较低或优化。您
int val;
// ...
val = (val != 0) ? otherVal : 0;
效率低于此:
int val;
//...
if (val != 0)
val = otherVal;
?
编译器是否能够优化三元运算符?意图很清楚,是否有任何方法可以将0写入内存?也许当内存映射到文件时
我们能假设这不重要吗
编辑:要点是在满足一个条件的情况下将变量设置为某个值。没有其他的分支。这就是为什么我问一个三元结构(带有必须复制的else分支)是否效率较低或优化。您的编译器将对其进行优化。最后,性能几乎没有差别 然而,在可读性方面有很大的不同。有时,三元运算符可以帮助删除许多不太清晰的代码行 在其他情况下,
if
语句更清晰,更容易理解
将代码简化为三元语句,但为了保持清晰性而不得不添加大量注释,这会适得其反
所有的编码之神都说,请不要嵌套三元语句。这主要是对 对于大多数编译器来说,效率是相同的,编译器将优化三元运算符,就像它优化if/else语句一样。也就是说,我更喜欢if语句,因为它们使代码更易于快速阅读 回答你的其他问题。我不确定你的意思,如果你只是将一个整数或变量设置为0,那么除了像上面那样将其设置为0之外,没有其他更快的方法 如果您有一个变量数组,可以使用
memset(ptr,0,size*sizeof(TYPE))
,如果您有一个要设置为零的变量数组,这可能是最快的。或者std::fill\n
我不确定你想用上面的逻辑实现什么,但这似乎有点奇怪。有一些方法可以安排代码,这意味着您可能根本不需要一个条件,但是如果不看到更多的代码,很难说您的情况
老实说,除非你做了几十亿次这个操作,否则这可能是非常早熟的优化,你应该专注于可读性。Mats-Peterson的建议通常是最好的“编写最可读的变体”。
但是,如果您试图编写最佳速度性能代码,则需要了解有关计算机和处理器的更多信息。对于某些机器,第一台运行得更快(高度流水线处理器:无分支、优化的三值运算符)。其他机器使用第二种形式(更简单)运行得更快。您可以使用无分支三值运算符,有时称为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);
}
内联静态int位选择(int条件、int truereturnvalue、int falsereturnvalue)
{
return(truereturnvalue&-condition)|(FALSE returnvalue&~(-condition));//a为真,b为假
}
内联静态浮点位选择(int条件、浮点truereturnvalue、浮点FalsReturnValue)
{
//重新解释浮动。会起作用,因为它只是一个选择,不管实际值如何
int&at=重新解释(truereturnvalue);
int&af=重新解释(falsereturnvalue);
int res=(at&-condition)|(af&~(-condition));//a为真,b为假
返回重新解释(res);
}
像“编译器资源管理器”这样的工具非常擅长回答这样的问题。修复了代码中的错误,我们看到它们在-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
Duplicit问题:我不是一个编译器程序员,所以我真的不知道,但它可能会计算三元运算符的两边,从而跳过分支(这会使它变得很快)。如果有任何问题,val在已经为零时设置为0,这在第一种情况下是不必要的。编译器不太可能做任何不同的事情。编写可读性最高的变量。@Hassedev否,
?:
条件运算符不允许同时计算其两个参数。@unwind Ok,感谢您的澄清。我记得它确实评估了GLSL的两个方面,但是不知道它对CPU和C++的作用。C++中你可以想象拷贝分配的发生。例如:“对象a=对象(默认);a=(条件)?对象(变量):对象(默认)”。编译器是否足够聪明和积极,足以删除赋值?听起来很危险,因为在构造函数中可能会发生一些事情。对于普通的旧数据会有什么不同吗?“不要嵌套三元语句”。。。除非您别无选择(constexpr
),否则有一种称为“复制省略”的优化,允许编译器防止一些不必要的复制。我认为对象(…)永远不会被构建,因为它是一个无名的temp,并且被分配给一个变量。只是想了解可读性对性能的影响。在某些情况下,使用三元可能会提高可读性,但也会导致性能成本。这意味着if赋值在逻辑上会更快,但如果我想使用三元来提高可读性,我会反对这种简单的逻辑。如果我这样做,编译器能优化它吗?很明显,三元else分支对于PoD是无用的。但同样,当文件映射到内存或对驱动程序进行编程时,每次写入内存都会产生影响。。。我需要了解大多数编译器在这方面是如何工作的。大多数编译器都会优化ternar