我对以下代码段的流程有一个简单的疑问。我在高级和汇编指令级别比较了这个代码块。我发现?:
远比分支好。
const int THUMB_IMAGE = 0;
const int ICON_IMAGE = 1;
const int UNKNOWN_IMAGE = 2;
void foo( int nFlag ){
int CopyFlag = nFlag;
if( CopyFlag == THUMB_IMAGE )
CopyFLag = ICON_IMAGE; // Setting THUMB and ICON images to same level
// Executing rest of the code
}
void foo1( int nFlag ){
int CopyFlag = ( nFlag == THUMB_IMAGE ) ?
ICON_IMAGE : nFlag; // Setting THUMB and ICON images to same level
// Executing rest of the code
}
int main(void){
foo( THUMB_IMAGE );
foo1( THUMB_IMAGE );
return 0;
}
在上面的代码段中,有两个函数foo()
和foo1()
。这两个函数将两种图像类型设置为ICON_IMAGE
。
问题是如何实施作业和if()
?
哪些条件语句经过高度优化,if()
或三元运算符?:
?
在汇编代码级别,要翻译if()
,CMP
(分支)和MOV
指令是必要的。
对于?:
运算符,我认为有一些特殊指令,但完全避免了分支指令。
谁能说出哪种方法最好?
在foo()
中,无论if条件如何,第一个赋值操作都会完成。它可能不是一直需要的。
但是在foo1()
中,这是在?:
运算符部分完成的,可以避免不需要的赋值操作。我想知道foo()
或foo1()
中的代码是否已经过优化?
答案 0 :(得分:2)
如果没有优化,您将设置copyFlag
两次
您使用if
时只编写一次?
。相当于
使用?:
的{{1}}将是:
if
通过优化,我希望生成所有三种变体 或多或少相同的代码。
一般来说,你不应该担心这种事情。以上 所有,我不担心汇编程序;相同 两种情况都可以进行优化。 (有可能 写这个没有任何分支指令;不管那是不是 最好的解决方案是由编译器来决定,而不是你。) 优化器会处理它。你应该写代码 以最清晰的方式 - 在这种情况下,这意味着 三元运算符。
答案 1 :(得分:1)
在一个不错的编译器上,if
和?:
之间的效率绝对没有区别。
这两种做同样事情的方法之间的唯一区别是,?:
包含微妙的促销规则。第二个和第三个表达式相互平衡,就好像它们是相同操作的操作数一样。如果您运气不好,这可能会导致错误,但可能不会使?:
的效率低于if
。
例如,如果您编写int x = 1;
然后x ? 1 : 1.0f;
,则结果类型为float
,这可能是意料之外的。
答案 2 :(得分:1)
对于 x86 系列,优化为off的编译器会将if... else...
转换为带有 x86 中跳转指令的分支,并将...?...:...
转换为有条件移动 x86 中的说明。
如果您在代码中直接编写条件,那么优秀的编译器会将分支优化为条件移动。
对于性能,如果很容易预测分支会更好,如果很难预测,条件移动会更好。原因是如果预测不正确,分支指令会在现代处理器中产生显着的分支预测损失,而条件移动指令的延迟稍微慢一些,无论条件是否成立。
有关详细解释,请参阅我的回答here:
答案 3 :(得分:1)
我100%确定任何现代编译器都会优化这两者。当然,在这个特定的例子中,编译器可能会删除foo
和foo1
的所有代码,因为这些在这个例子中什么都不做,除非你关闭优化 - 这不是一个公平的比较。您需要一个更复杂的示例,其中编译器无法确定输入并需要代码的结果。
现在,如果我对这段代码的作用是正确的 - 即复制某种图像 - 那么我希望函数开头的if语句对于这个代码花费的总时间非常少功能。
与性能一样,首先分析您的代码并识别“热点”,然后集中精力使热点运行得更快。我很确信这个不是热点。因此,您正在尝试优化不会对代码产生重大影响的内容。
此外,不要相信互联网上有人告诉你哪个版本的代码更快 - 在不同的配置中运行它然后做出决定。最好是在具有不同硬件的几台机器上(不同的制造商处理器,在这种特殊情况下) - 除非它是一个只需要在当前计算机上快速运行的爱好项目。
答案 4 :(得分:0)
这取决于编译器。一些编译器将优化两个语句并从中生成相同的汇编代码。
作为旁注,使用最快的解决方案的优势非常小,不值得麻烦。如果您调用此代码来设置图像的类型,则很可能您将在某处使用该图像(即使只更改图像类型而不显示它们),这需要很多更多的CPU周期而不是if
或?:
语句中的额外汇编指令。使用三元运算符?:
编写代码可能会使开发和维护更加困难,而且不值得付出努力。
编辑:
性能上的差异来自于foo
为nFlag
时您在THUMB_IMAGE
中执行两项作业的事实。以下foo2
应该与foo1
的性能更接近。
void foo2( int nFlag ){
int CopyFlag;
if( nFlag == THUMB_IMAGE )
CopyFlag = ICON_IMAGE; // Setting THUMB and ICON images to same level
else
CopyFlag = nFlag;
// Executing rest of the code
}
性能上的差异可能是编译器未构建以优化赋值,而if
语句将同一变量赋值为if-else
语句(例如Sun / Oracle) Java编译器这样做)或者可能已经指示编译器不要优化代码(通过编译标志/参数)。
答案 5 :(得分:0)
理论上应该没有任何区别,理论上,无所不知的优化器会将其优化为相同的生成代码。
在实践中,我知道至少amd64上的某些gcc版本会生成if
的分支,并使用cmov
指令与:?
无关,无论优化级别和标志如何。听起来像一个小东西,但在代码库中,我在一个类似于此的构造上测试它,在一种锁中使用if
更改为:?
使cpu使用率减少了10%在宏观基准上。
当然,您的里程可能会有所不同,具体取决于编译器,编译器版本,标志和您控制之外的其他内容。因此,请对您的编译器进行测试,看看它是否值得优化。
答案 6 :(得分:0)
使用优化编译器,您不应该看到任何差异。只需使用更具可读性的东西。
答案 7 :(得分:0)
所有其他答案,表明一个体面的编译器不关心,并且您可能不应该担心,除非您在分析器中看到此函数作为性能瓶颈弹出,是完全正确的。
但是......如果你在一些分支机构很昂贵的架构上,并且条件移动不存在,或者你的编译器非常糟糕......你可以将天真版本与以下内容进行比较:
void foo( int nFlag ){
int CopyFlag = ((nFlag & 2) << 1) + 1;
// Rest of code...
显然需要提出很多警告(它很丑陋,黑客,不可维护等等)......但它会并行化!
我真正得到的是,不是担心两个相似语言结构的确切代码,你应该(在确定你甚至有性能问题之后)看看算法本身,以及是否是否可以更改或替换它以避免完全出现问题。
答案 8 :(得分:0)
只有在关闭优化时才会存在性能差异。现代编译器应该平等地优化两个版本。
选择其中一种解决方案的原因可能是:
if
语句可以让您了解两个分支是否都经过测试。三元运算符没有。其中一个表达式在程序运行期间根本无法评估,但代码覆盖率工具将向您显示所有内容。