x >= 0 比 x >= 更有效率吗-1?
在 C++ 中与 int 进行比较 x >= 0
比 x >= 更高效。 -1?
Doing a comparison in C++ with an int is x >= 0
more efficient than x > -1
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
简短的回答:不。
更长的答案提供一些教育见解:这完全取决于您的编译器,尽管我打赌每个理智的编译器都会为这两个表达式创建相同的代码。
示例代码:
然后编译并比较生成的汇编代码:
产生:
与
(编译器:g++-4.4)
结论:不要试图比编译器更聪明,专注于算法和数据结构,基准测试和分析真正的瓶颈,如果有疑问:检查编译器的输出。
short answer: no.
longer answer to provide some educational insight: it depends entirely on your compiler, allthough i bet that every sane compiler creates identical code for the 2 expressions.
example code:
and then compile and compare the resulting assembler code:
yields this:
vs.
(compiler: g++-4.4)
conclusion: don't try to outsmart the compiler, concentrate on algorithms and data structures, benchmark and profile real bottlenecks, if in doubt: check the output of the compiler.
您可以查看生成的汇编代码,这些代码可能因架构而异,但我敢打赌,两者的生成代码都需要完全相同的周期。
而且,正如评论中提到的 - 最好写出最容易理解的内容,当您有真正测量的瓶颈时进行优化,您可以使用分析器来识别这些瓶颈。
顺便说一句:正确地提到,如果
x
是无符号
,则x>-1
可能会导致问题。它可能会隐式转换为signed
(尽管您应该收到警告),这会产生不正确的结果。You can look at the resulting assembly code, which may differ from architecture to architecture, but I would bet that the resulting code for either would require exactly the same cycles.
And, as mentioned in the comments - better write what's most comprehensible, optimize when you have real measured bottlenecks, which you can identify with a profiler.
BTW: Rightly mentioned, that
x>-1
may cause problems ifx
isunsigned
. It may be implicitly cast intosigned
(although you should get a warning on that), which would yield incorrect result.记住:测量。并且不要过早优化!
除了测量之外别无选择。
您需要记住,您所测量的只是:一组测量结果不一定会告诉您任何一般情况,而只是一个特定的结果。当然,提及如此明显的事情可能听起来很居高临下。所以,好吧,就这样吧:只需测量即可。
或者,我是否应该提到大多数处理器都有一条用于与零进行比较的特殊指令,但这并不允许人们得出有关代码片段性能的任何结论?
**EDIT**: an amendment with the points mentioned by @MooingDuck in the commentary.
问题:
> Doing a comparison in C++ with an `int` is `x >= 0` more efficient than `x > -1`?
这个问题有什么问题
经典三卷本《计算机编程的艺术》的作者 Donald Knuth 曾经写道,
效率如何
x >= 0< /code> 与
x > 进行比较-1
通常是无关紧要的。也就是说,关注的焦点很可能是错误的。如何清楚地表达您想说的内容更为重要。您和其他人维护此代码的时间通常比程序的执行时间重要得多。关注代码与其他程序员沟通的情况,即关注清晰度。
为什么问题的焦点是错误的
清晰度会影响正确的机会。如果不需要正确,任何代码都可以任意快。因此,正确性是最重要的,并且意味着清晰度非常重要——比减少一纳秒的执行时间重要得多……
而且这两个表达式不等效。清晰度和文字。他们正确的机会。
如果
x
是有符号整数,则x >= 0
与x >= 完全相同。 -1
。但如果x
是一个无符号整数,例如unsigned
类型,则x > -1
表示x > static_cast(-1)
(通过隐式提升),这又意味着x > std::numeric_limits::max()
。这大概不是程序员想要表达的意思!焦点错误的另一个原因(它是在微观效率上,而应该是在清晰度上)是对效率的主要影响通常不是来自单个操作的时间安排(除了在某些情况下来自动态分配和甚至更慢的操作)磁盘和网络操作),但来自算法效率。例如,编写...
效率非常低,因为它使用的时间与n的平方成正比,O(n2), 二次时间。
但改为写作……
将时间减少到与n、O(n)、线性时间成正比。
由于关注于各个操作时序,现在人们可能会考虑编写
'-'
而不是"-"
以及此类愚蠢的细节。相反,在注重清晰度的情况下,人们会专注于使代码比循环更清晰。例如,通过使用适当的string
构造函数:哇!
最后,不要为小事情操心的第三个原因是,一般来说,它只是代码的一小部分,对执行时间的贡献不成比例。仅通过分析代码来识别该部分(或多个部分)并不容易。需要进行测量,这种“它把时间花在哪里”的测量称为分析。
如何找出问题的答案
二十或三十年前,人们只需查看生成的机器代码就可以对各个操作的效率有一个合理的了解。
例如,您可以通过在调试器中运行程序来查看机器代码,或者使用适当的选项要求编译器生成汇编语言列表。 g++ 注意:选项
-masm=intel
可以方便地告诉编译器不要生成不可处理的 AT&T 语法汇编,而是生成 Intel 语法汇编。例如,微软的汇编器使用扩展的英特尔语法。今天计算机的处理器更加智能。它可以无序执行指令,甚至可以在“当前”执行点需要它们的效果之前执行指令。编译器也许能够预测这一点(通过结合从测量中收集到的有效知识),但人类几乎没有机会。
因此,普通程序员唯一的办法就是测量。
测量,测量,测量!
一般来说,这涉及执行要测量的事情无数次,然后除以无数次。
否则启动时间和停机时间将占主导地位,结果将是垃圾。
当然,如果生成的机器代码相同,那么测量不会告诉您任何有关相对差异的有用信息。它只能表明测量误差有多大。因为那时你就知道差异应该为零。
为什么测量是正确的方法
假设 SO 答案中的理论考虑表明 x >= -1 会比 x >= -1 慢。 0 。
编译器可以通过为
x > 生成糟糕的代码来击败任何此类理论上的考虑。 0
,也许是由于它随后(不幸的是!)认识到了上下文“优化”机会。计算机的处理器同样也会使预测变得混乱。
所以无论如何你都必须测量。
这意味着理论上的考虑没有告诉您任何有用的信息:无论如何您都会做同样的事情,即测量。
参考文献:
[1] 高德纳 (Knuth),唐纳德 (Donald)。 使用 go to 语句进行结构化编程,ACM 期刊计算调查,第 6 卷,第 4 期,1974 年 12 月。第 268 页。
Remember: measure. And don't optimize prematurely!
There is no alternative to measuring.
You need to keep in mind that what you're measuring is just that: that a single set of measurements does not necessarily tell you anything in general, but just a specific result. Of course it might sound patronizing to mention such obvious things. So, OK, let that be it: just measure.
Or, should I perhaps mention that most processors have a special instruction for comparing against zero, and yet that that does not allow one to conclude anything about performance of your code snippets?
**EDIT**: an amendment with the points mentioned by @MooingDuck in the commentary.
The question:
> Doing a comparison in C++ with an `int` is `x >= 0` more efficient than `x > -1`?
What’s wrong with the question
Donald Knuth, author of the classic three volume work The Art of Computer Programming, once wrote[1],
How efficient
x >= 0
is compared tox > -1
is most often irrelevant. I.e. it’s most likely a wrong thing to focus on.How clearly it expresses what you want to say, is much more important. Your time and the time of others maintaining this code is generally much more important than the execution time of the program. Focus on how well the code communicates to other programmers, i.e., focus on clarity.
Why the focus of the question is wrong
Clarity affects the chance of correctness. Any code can be made arbitrarily fast if it does not need to be correct. Correctness is therefore most important, and means that clarity is very important – much more important than shaving a nano-second of execution time…
And the two expressions are not equivalent wrt. clarity, and wrt. to their chance of being correct.
If
x
is a signed integer, thenx >= 0
means exactly the same asx > -1
. But ifx
is an unsigned integer, e.g. of typeunsigned
, thenx > -1
meansx > static_cast<unsigned>(-1)
(via implicit promotion), which in turn meansx > std::numeric_limits<unsigned>::max()
. Which is presumably not what the programmer meant to express!Another reason why the focus is wrong (it’s on micro-efficiency, while it should be on clarity) is that the main impact on efficiency comes in general not from timings of individual operations (except in some cases from dynamic allocation and from the even slower disk and network operations), but from algorithmic efficiency. For example, writing …
is pretty inefficient, because it uses time proportional to the square of n, O(n2), quadratic time.
But writing instead …
reduces the time to proportional to n, O(n), linear time.
With the focus on individual operation timings one could be thinking now about writing
'-'
instead of"-"
, and such silly details. Instead, with the focus on clarity, one would be focusing on making that code more clear than with a loop. E.g. by using the appropriatestring
constructor:Wow!
Finally, a third reason to not sweat the small stuff is that in general it’s just a very small part of the code that contributes disproportionally to the execution time. And identifying that part (or parts) is not easy to do by just analyzing the code. Measurements are needed, and this kind of "where is it spending its time" measurement is called profiling.
How to figure out the answer to the question
Twenty or thirty years ago one could get a reasonable idea of efficiency of individual operations, by simply looking at the generated machine code.
For example, you can look at the machine code by running the program in a debugger, or you use the approiate option to ask the compiler to generate an assembly language listing. Note for g++: the option
-masm=intel
is handy for telling the compiler not to generate ungrokkable AT&T syntax assembly, but instead Intel syntax assembly. E.g., Microsoft's assembler uses extended Intel syntax.Today the computer's processor is more smart. It can execute instructions out of order and even before their effect is needed for the "current" point of execution. The compiler may be able to predict that (by incorporating effective knowledge gleaned from measurements), but a human has little chance.
The only recourse for the ordinary programmer is therefore to measure.
Measure, measure, measure!
And in general this involves doing the thing to be measured, a zillion times, and dividing by a zillion.
Otherwise the startup time and take-down time will dominate, and the result will be garbage.
Of course, if the generated machine code is the same, then measuring will not tell you anything useful about the relative difference. It can then only indicate something about how large the measurement error is. Because you know then that there should be zero difference.
Why measuring is the right approach
Let’s say that theoretical considerations in an SO answer indicated that
x >= -1
will be slower thanx > 0
.The compiler can beat any such theoretical consideration by generating awful code for that
x > 0
, perhaps due to a contextual "optimization" opportunity that it then (unfortunately!) recognizes.The computer's processor can likewise make a mess out of the prediction.
So in any case you then have to measure.
Which means that the theoretical consideration has told you nothing useful: you’ll be doing the same anyway, namely, measuring.
References:
[1] Knuth, Donald. Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974. p.268.
您的编译器可以自由决定如何实现这些(使用哪些汇编指令)。正因为如此,没有什么区别。一个编译器可以实现
x > -1
为x >= 0
,另一个可以将x >= 0
实现为x >= 0
。 -1。如果有任何差异(不太可能),您的编译器将选择更好的一个。Your compiler is free to decide how to implement those (which assembly instructions to use). Because of that, there is no difference. One compiler could implement
x > -1
asx >= 0
and another could implementx >= 0
asx > -1
. If there is any difference (unlikely), your compiler will pick the better one.它们应该是等价的。两者都将被编译器翻译成单个汇编指令(忽略两者都需要将 x 加载到寄存器中)。在任何现代处理器上都有“大于”指令和“大于或等于”指令。由于您将其与恒定值进行比较,因此将花费相同的时间。
不要为微小的细节而烦恼,找到大的性能问题(例如算法设计)并解决这些问题,看看阿姆达尔定律。
They should be equivalent. Both will be translated by the compiler into a single assembly instruction (neglecting that both will need to load x into a register). On any modern day processor there is a 'greater-than' instruction and a 'greater-than-or-equal' instruction. And since you are comparing it to a constant value, it will take the same amount of time.
Don't fret over the minute details, find the big performance problems (like algorithm design) and attack those, look at Amdahls Law.
我怀疑是否存在任何可测量的差异。编译器应该发出一些带有跳转指令的汇编代码,例如
JAE
(如果高于或等于则跳转)或JA
(如果高于则跳转)。这些指令可能跨越相同数量的周期。最终,这并不重要。只需使用代码的人类读者更清楚的内容即可。
I doubt there is any measurable difference. The compiler should emit some assembled code with a jump instruction such as
JAE
(jump if above or equal) orJA
(jump if above). These instructions likely span the same number of cycles.Ultimately, it doesn't matter. Just use what is more clear to a human reader of your code.