x >= 0 比 x > -1 更有效吗?
Is x >= 0 more efficient than x > -1?
在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:正确地提到,如果x
是unsigned
,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()
。这大概不是程序员想要表达的!
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
(如果高于则跳转)。这些指令可能跨越相同数量的循环。
最终,这并不重要。只要使用对人类读者来说更清楚的代码就可以了。
- 欧拉项目#8答案是大以获得有效答案
- 调整大小后指向元素值的指针unordered_map有效?
- 为什么是0;C++中的有效语句
- 最高有效数字侧的第N位
- GCC对可能有效的代码抛出init list生存期警告
- 有效地使用std::unordered_map来插入或增加键的值
- c++中O(n^(1/3))中一个数的除数的有效计数
- 使用无符号字符数组有效存储内存
- 自定义先决条件对移动分配运算符有效吗
- EASTL矢量<向量<int>>连续的
- 为什么将值返回函数传递给重载=运算符对运算符函数有效,而对其他运算符无效
- 有哪些有效的方法可以消除一组 100 万个字符串>重复数据?
- 为什么这种直接初始化有效?(C++17)
- 递归函数有效,但无法记忆
- 在C++中初始化向量映射的最有效方法
- 如果变量名称不跟在 char* 后面,const char* 是否有效?
- 钳制迭代器是否有效
- 如何有效地在 std::vector 中插入一对?
- C++模板 t 不是有效的模板类型
- 检查由括号、方括号和大括号组成的一组方括号是否有效?