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

Is x >= 0 more efficient than x > -1?

本文关键字:gt 有效      更新时间:2023-10-16

在c++中使用int比较x >= 0是否比x > -1更有效?

简短回答:no.

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

示例代码:

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

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

% gcc -S -O2 - form -frame-pointer foo。cc

收益率:

<>以前_Z8func_ge0i:.LFB0:.cfi_startproc.cfi_personality 0 x0, __gxx_personality_v0移动4(%esp), %eaxnotl % eax税$31 %受潮湿腐烂.cfi_endproc与

<>以前_Z9func_gtm1i:.LFB1:.cfi_startproc.cfi_personality 0 x0, __gxx_personality_v0移动4(%esp), %eaxnotl % eax税$31 %受潮湿腐烂.cfi_endproc(编译器:g++ - -4.4)

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

您可以查看结果汇编代码,它可能因体系结构而异,但我敢打赌,两者的结果代码都需要完全相同的循环。

并且,正如在评论中提到的——最好写最容易理解的东西,当您有真正可测量的瓶颈时进行优化,您可以使用分析器识别。

BTW:正确地提到,如果xunsigned,x>-1可能会引起问题。它可能隐式地强制转换为signed(尽管您应该得到警告),这将产生不正确的结果。

上次我回答这样的问题时,我只是写了"measure",并填了句号,直到我接受了。

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

然而,除了测量,没有其他选择。

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

为了足够详细地讨论这个问题,使答案不只是被否决和删除,你需要记住,你所测量的只是:一组测量不一定能告诉你任何一般的东西,而只是一个特定的结果。当然,提到这些显而易见的事情可能听起来很傲慢。所以,好吧,就这样吧:只是测量。

或者,我是否应该提到大多数处理器都有一个特殊的指令来与零进行比较,然而这并不允许人们得出关于代码片段性能的任何结论?好了,我想我就讲到这里吧。记住:衡量。不要过早地优化!



编辑:修改@MooingDuck在评论中提到的要点。

问题:

在c++中与int进行比较x >= 0是否比x > -1更有效?

这个问题有什么问题

Donald Knuth,经典三卷本《计算机程序设计艺术》的作者,曾经写道[1],

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

x > -1相比,效率x >= 0通常是不相关的。也就是说,它很可能是一个错误的关注。

如何清楚地表达你想说的,是更重要的。您和其他人维护这些代码的时间通常比程序的执行时间重要得多。关注代码与其他程序员的沟通情况,也就是说,关注代码的清晰度。

为什么问题的焦点不对

清晰影响正确的机会。任何代码都可以任意快速,如果它不需要是正确的。因此,正确性是最重要的,这意味着清晰是非常重要的。比减少纳秒的执行时间更重要& help;

两个表达式不是等价的wrt。清晰,和写作。为他们答对的机会干杯

如果x是有符号整数,则x >= 0的含义与x > -1完全相同。但是如果x是一个无符号整数,例如类型为unsigned,则x > -1表示x > static_cast<unsigned>(-1)(通过隐式提升)),即x > std::numeric_limits<unsigned>::max()。这大概不是程序员想要表达的!

另一个错误的原因是,对效率的主要影响通常不是来自单个操作的计时(除了在某些情况下来自动态分配和更慢的磁盘和网络操作),而是来自算法效率。例如,写& help;
string s = "";
for( int i = 0;  i < n;  ++i ) { s = s + "-"; }

效率很低,因为它使用的时间正比于n的平方,O(n2),二次时间.

而是写& help;

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

将时间减小成正比于n, O(n),线性时间.

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

string s( n, '-' );

哇!

最后,不为小细节操心的第三个原因是,一般来说,它只是代码中非常小的一部分,对执行时间的贡献不成比例。并且仅仅通过分析代码来识别该部分(或多个部分)并不容易。测量是需要的,这种"它把时间花在哪里"的测量被称为分析.

如何找出问题的答案

二三十年前,人们可以通过简单地查看生成的机器代码,对单个操作的效率有一个合理的了解。

例如,您可以通过在调试器中运行程序来查看机器代码,或者您可以使用适当的选项来要求编译器生成汇编语言清单。注意:选项-masm=intel可以方便地告诉编译器不要生成不可grokable的AT&T语法汇编,而是生成Intel语法汇编。例如,Microsoft的汇编程序使用扩展的Intel语法。

今天计算机的处理器更聪明了。它可以乱序执行指令,甚至在"当前"执行点需要它们的效果之前执行指令。编译器可能能够预测(通过结合从测量中收集的有效知识),但是人类几乎没有机会。

对于普通程序员来说,唯一的办法就是测量

。.测量,测量,测量!

一般来说,这包括做要测量的事情,无数次,然后除以无数次。

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

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

为什么测量是正确的方法

让我们说,在SO答案中的理论考虑表明,x >= -1将比x > 0慢。

编译器可以通过为x > 0生成糟糕的代码来击败任何这样的理论考虑,可能是由于它随后(不幸的!)识别出上下文"优化"机会。

计算机的处理器同样可以把预测搞得一团糟。

所以无论如何你都要测量。

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

为什么这个详尽的答案,虽然显然有帮助,但我真的没有

就我个人而言,我更喜欢"测量"这个词。作为答案。

因为这就是最终的结果。

其他的事情,读者不仅可以自己弄清楚,而且无论如何都必须弄清楚细节。因此,在这里试图描述它只是多余的,真的。

引用:

[1]陈志强。结构化编程与go语句,ACM计算调查杂志,第6卷,第4期,1974年12月。p.268 .

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

它们应该是相等的。两者都将被编译器翻译成一条汇编指令(忽略两者都需要将x加载到寄存器中)。在任何现代处理器上都有一个"大于"指令和一个"大于或等于"指令。由于您将其与一个常数值进行比较,因此将花费相同的时间。

不要为微小的细节烦恼,找到大的性能问题(如算法设计)并解决这些问题,看看Amdahls定律。

我怀疑有任何可测量的差异。编译器应该发出一些带有跳转指令的汇编代码,例如JAE(如果高于或等于则跳转)或JA(如果高于则跳转)。这些指令可能跨越相同数量的循环。

最终,这并不重要。只要使用对人类读者来说更清楚的代码就可以了。