x> = 0比x>更有效吗? -1?

时间:2011-11-29 19:10:46

标签: c++ optimization micro-optimization

在C ++中使用int进行比较x >= 0x > -1效率更高?

6 个答案:

答案 0 :(得分:19)

简短回答:不。

更长的答案提供一些教育见解:它完全取决于你的编译器,尽管我敢打赌每个理智的编译器为2个表达式创建相同的代码。

示例代码:

int func_ge0(int a) {
    return a >= 0;
}   

int func_gtm1(int a) {
    return a > -1; 
}

然后编译并比较生成的汇编代码:

   % gcc -S -O2 -fomit-frame-pointer foo.cc

得出这个:

_Z8func_ge0i:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    movl    4(%esp), %eax
    notl    %eax
    shrl    $31, %eax
    ret
    .cfi_endproc

VS

_Z9func_gtm1i:
.LFB1:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    movl    4(%esp), %eax
    notl    %eax
    shrl    $31, %eax
    ret
    .cfi_endproc

(编译器:g ++ - 4.4)

结论:如果有疑问,请不要试图超越编译器,专注于算法和数据结构,基准测试和配置真正的瓶颈:检查编译器的输出。

答案 1 :(得分:7)

您可以查看生成的汇编代码,这可能因架构而异,但我敢打赌,任何一个代码都需要完全相同的循环。

并且,如评论中所述 - 更好地编写最易于理解的内容,在有实际测量瓶颈时进行优化,您可以使用分析器识别这些瓶颈。

顺便说一句:如果x>-1x,则unsigned可能会导致问题。它可能会隐式地转换为signed(尽管你应该收到警告),这会产生不正确的结果。

答案 2 :(得分:6)

我最后一次回答这个问题时,我刚刚写了"测量",并填写了句号直到SO接受它。

该答案在几分钟内被投票3次,并被SO主持人删除(以及该问题的至少一个其他答案)。

但是,除了测量之外别无选择。

所以这是唯一可能的答案。

为了充分详细地继续讨论这个问题,答案不只是被低估和删除,你需要记住,你所测量的只是:一组测量并不一定会告诉你任何事情,只是一个特定的结果。当然,提到这些显而易见的事情可能听起来很光顾。那么,好吧,就这样吧:只是衡量。

或者,我是否应该提一下大多数处理器都有一个特殊的指令来比较零,但是那不能让你得到关于代码片段性能的任何结论?

嗯,我想我就止在那里。记住:衡量。并且不要过早优化!

<小时/>
编辑对@MooingDuck在评论中提及的要点进行修正。

问题:

  

使用int进行C ++比较,x >= 0的效率是否x > -1

问题

有什么问题 Donald Knuth,经典三卷作品“计算机编程艺术”的作者,曾写过[1],

  

“我们应该忘记效率低,大约97%的时间说:过早的优化是所有邪恶的根源”

如何将效率 x >= 0x > -1进行比较通常无关紧要。即重点关注的可能是错误的。

如何明确地表达你想说的话,更为重要。您维护此代码的时间和时间通常比程序的执行时间重要得多。关注代码与其他程序员的沟通情况,即注重清晰度。

为什么问题的焦点是错误的

清晰度会影响正确性。如果不需要正确的话,任何代码都可以任意快速地制作。因此,正确性是最重要的,并且意味着清晰度非常重要 - 比剃须纳秒执行时间更重要...

这两个表达式是不是等效的wrt。清晰度和wrt。他们正确的机会。

如果x是有符号整数,则x >= 0表示与x > -1完全相同。但是,如果x是无符号整数,例如类型unsigned,然后x > -1表示x > static_cast<unsigned>(-1)(通过隐式促销),这反过来意味着x > std::numeric_limits<unsigned>::max()。这可能不是程序员想表达的意思!

焦点错误的另一个原因(它是微观效率,而应该是明确的)是对效率的主要影响通常不是来自个别运营的时间(除了在某些情况下来自动态分配和来自更慢的磁盘和网络操作),但来自算法效率。例如,写...

string s = "";
for( int i = 0;  i < n;  ++i ) { s = s + "-"; }

是非常低效的,因为它使用的时间与 n 的平方成正比,O( n 2 ),二次时间

但是写作......

string s = "";
for( int i = 0;  i < n;  ++i ) { s += "-"; }

缩短与 n ,O( n ),线性时间成比例的时间。

关注个人操作时间,现在可以考虑编写'-'而不是"-",以及如此愚蠢的细节。相反,在关注清晰度的情况下,人们会专注于使代码比循环更清晰。例如。使用适当的string构造函数:

string s( n, '-' );

哇!

最后,第三个不让小东西流汗的原因是,通常它只是代码的一小部分,它对执行时间的贡献不成比例。通过分析代码来识别那部分(或部分)并不容易。需要进行测量,这种&#34;它在哪里花费时间&#34;测量被称为分析

如何找出问题的答案

二十或三十年前,只需查看生成的机器代码,就可以合理地了解单个操作的效率。

例如,您可以通过在调试器中运行程序来查看机器代码,或者使用approiate选项要求编译器生成汇编语言列表。关于g ++的注意事项:选项-masm=intel对于告诉编译器不要生成不可驱动的AT&amp; T语法汇编而非生成英特尔语法汇编非常方便。例如,Microsoft的汇编程序使用扩展的Intel语法。

今天,计算机的处理器更加智能。它可以不按顺序执行指令,甚至可以在“#current;&#34;执行点。编译器可能能够预测(通过结合从测量中收集到的有效知识),但人类几乎没有机会。

普通程序员的唯一办法是衡量

测量,测量,测量!

总的来说,这涉及做一件有待衡量的事情,数十亿次,并且除以数十亿次。

否则启动时间和取下时间将占主导地位,结果将是垃圾。

当然,如果生成的机器代码相同,那么测量不会告诉您有关相对差异的任何有用信息。然后,它只能指示测量误差有多大。因为你知道那应该是零差异。

为什么测量是正确的方法

假设SO答案中的理论考虑表明x >= -1将慢于x > 0

编译器可以通过为x > 0生成可怕的代码来击败任何这样的理论考虑,可能是由于上下文&#34;优化&#34;然后(不幸的是!)认识到的机会。

计算机的处理器同样可以弄清楚预测。

所以无论如何你必须要衡量。

这意味着理论上的考虑并没有告诉你任何有用的东西:无论如何你都会做同样的事情,即测量。

为什么这个详细的回答,虽然显然有帮助,但恕我直言

我个人更喜欢单词“measure”作为答案。

因为这是归结为它的原因。

其他任何读者不仅可以自己弄明白,但必须弄清楚无论如何都要详细说明 - 所以这只是用来描述它在这里,真的。

参考文献:

[1] Knuth,唐纳德。 Structured Programming with go to Statements,ACM Journal Computing Surveys,Vol 6,No。4,1974年12月.p.268。

答案 3 :(得分:3)

您的编译器可以自由决定如何实现这些(使用哪些汇编指令)。因此,没有区别。一个编译器可以将x > -1实现为x >= 0,另一个编译器可以将x >= 0实现为x > -1。如果存在任何差异(不太可能),您的编译器将选择更好的。

答案 4 :(得分:2)

它们应该是等价的。两者都将由编译器转换为单个汇编指令(忽略两者都需要将x加载到寄存器中)。在任何现代处理器上都有一条“大于”的指令和一条“大于或等于”的指令。而且由于您将其与常量值进行比较,因此需要相同的时间。

不要担心细节,找到大的性能问题(比如算法设计)并攻击那些,看看Amdahls Law。

答案 5 :(得分:1)

我怀疑有任何可衡量的差异。编译器应使用a jump instruction发出一些汇编代码,例如JAE(如果大于或等于则跳转)或JA(如果超过则跳转)。这些指令可能跨越相同的周期数。

归根结底,这并不重要。只需使用人类读者对代码更清楚的内容。