在C ++中使用int进行比较x >= 0
比x > -1
效率更高?
答案 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>-1
为x
,则unsigned
可能会导致问题。它可能会隐式地转换为signed
(尽管你应该收到警告),这会产生不正确的结果。
答案 2 :(得分:6)
我最后一次回答这个问题时,我刚刚写了"测量",并填写了句号直到SO接受它。
该答案在几分钟内被投票3次,并被SO主持人删除(以及该问题的至少一个其他答案)。
但是,除了测量之外别无选择。
所以这是唯一可能的答案。
为了充分详细地继续讨论这个问题,答案不只是被低估和删除,你需要记住,你所测量的只是:一组测量并不一定会告诉你任何事情,只是一个特定的结果。当然,提到这些显而易见的事情可能听起来很光顾。那么,好吧,就这样吧:只是衡量。
或者,我是否应该提一下大多数处理器都有一个特殊的指令来比较零,但是那不能让你得到关于代码片段性能的任何结论?
嗯,我想我就止在那里。记住:衡量。并且不要过早优化!<小时/>
使用
int
进行C ++比较,x >= 0
的效率是否x > -1
?
“我们应该忘记效率低,大约97%的时间说:过早的优化是所有邪恶的根源”
如何将效率 x >= 0
与x > -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(
关注个人操作时间,现在可以考虑编写'-'
而不是"-"
,以及如此愚蠢的细节。相反,在关注清晰度的情况下,人们会专注于使代码比循环更清晰。例如。使用适当的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
(如果超过则跳转)。这些指令可能跨越相同的周期数。
归根结底,这并不重要。只需使用人类读者对代码更清楚的内容。