Does writing i != 0 compare faster or slower than i > 0 for a positive integer?
本问题已经有最佳答案,请猛点这里访问。
假设我有一个程序需要检查变量
这两个表达式的性能有区别吗?为什么?
我知道没有明显的表现差异,这更是一个哲学问题。
- 不,没有,别担心这种事了
- 我知道两者之间的区别,如果有的话,可能是微不足道的,但是每次我写下这些条件中的一个,这个问题仍然会浮现在我的脑海中。
- 如果存在性能差异,可能几乎无法测量。现在,编译器和CPU都非常好,我保证您不会看到任何不同。
- 我知道没有明显的区别,这更是一个哲学问题。
- 用这种比较方法测量没有什么不同,所以不要担心。您的大部分性能问题将是[A]循环,[B]I/O。集中在这些方面。
- 相关:stackoverflow.com/questions/24581474/…
- 抱歉,复制品是@false。我很确定这一定是在这里的某个地方,但是我的搜索结果却一无所获。
- 不,对不起,这不是真的复制品。你不是专门问一个循环,所以…
- 是的,但这是相同的想法。它是否在循环中并不影响答案。
- 在一些非常罕见的情况下,这样的事情确实会产生影响(如果它是重载数字处理算法或图形驱动程序的最内部循环和瓶颈,那么如果您的内部循环有5或6个机器指令(速度相差20%),它确实会产生影响,但是如果QA问到这个问题,这意味着这是n这里的情况。
- 你知道你在问什么指令集吗?什么处理器型号?
- @所以你是说,严格来说,有区别吗?
- @请注意:我认为可以肯定地说,
!= 永远不会比> 慢。 - @请注意:不,我的意思是可能会有非常具体的情况有区别,但它不太可能在你的情况下,除非你写的是设备驱动程序或其他机器级的东西。
- @本沃伊特,谢谢,这类回答正是我想要的。
- @我爸,我觉得你说的不对。计算
-(x != 0) (测试和否定)可能比计算-(x < 0) 慢得多,后者可以写为x >> 63 (或任何适当的移位)。这一切都取决于上下文和CPU体系结构。 - @我想我的意思是在一个实际使用条件进行分支的环境中,而不是在某个场景中使用布尔值进行整数运算或类似的模糊操作。
- @我不认为这真的很难懂。每当你写一些看起来像(或者可以重写成)
cond ? expr1 : expr2 的东西时,像llvm这样一个足够好的编译器/后端将尽其所能地以令人厌恶的、难以理解的方式将你的代码分开,以使其快速。 - @DFEuer:您是否有一个实际的示例(布尔值用于分支而不是算术)?
- @我不知道你的意思。显然,如果您只是使用循环终止的结果或者类似的结果,那就不太重要了。但在许多其他上下文中,代码可以用"分支"形式编写,可以编译成可能更好或不更好的非分支形式。
- @迪费尔:我的意思是,如果你这样看的话,你就永远不能声称任何东西比任何东西都快,因为编译器在决定如何编译你的代码时有充分的判断力,你永远不会知道,也许你会幸运地在一个蓝月里,编译器会做一些你从未梦想过的疯狂的事情。有了这种推理(在技术上是正确的),你就不能得出有意义的结论:什么更快,什么不更快。
- @在某种程度上,这是正确的。关心性能的程序员需要知道他们的编译器执行了什么样的大规模优化,在某些情况下还需要知道哪些类型的小规模更改可能很重要。C程序员需要了解使用有符号循环变量而不是无符号循环变量的优点(有时)。haskell程序员必须知道(有时)使用编译器重写规则将融合在一起的函数的优点,而不是手工编写的递归函数。
- @德弗尔:没错,我想说的是,我不知道在什么情况下,
!= 会比> 或< 更好。当然,您可能会因为编译器的错误以及表达式是如何根据上下文重新排列而幸运,但这是幸运(噪声),而不是您可以系统地期望的。这就是为什么我说!= 永远不会变慢的原因。但是如果你有一个反例,你可以证明期望更快(即,不仅仅是随机的侥幸),我会更高兴看到它,并从中学习一两件事。 - 即使你想知道,这怎么可能是语言不可知论?
我不认为它有很大的不同,但与流行的智慧相反,我要告诉你使用EDCOX1,0,而不是EDCOX1,1,EDCX1,2,因为前者是一个更一般的操作,如果你要把你的代码转换成C++,使用迭代器代替指针,并不是所有的迭代器都支持EDCOX1。2〕或
- 如果他用这个作为循环的检查,那将是违反常识的,但是。
- 过早,甚至没有优化。
!= 使用前向迭代器,但步幅不大于1。< 的工作步幅很大,但只使用随机访问迭代器。最好是上下文相关的。 - @迈克尔:我想。在任何情况下,我都将这种推理应用于循环和非循环,所以它对我来说并没有什么区别。
- @本沃伊特:如果你的步幅大于1,那么你已经在用EDOCX1[1]做错误了;迭代器将越界,因为你处于未定义的行为领域。
- @我想你的意思是溢出?但是,"打印所有小于10000的
N 的倍数"如何,上界是固定的?那么,< 是完美的。 iterators 是一个实现对象,imo。一旦它归根结底为机器代码,它就必须表示为a < b 或a != b ,即使对于迭代器也是如此(您检查了生成的程序集吗?)- @Mehrdad:为了避免迭代结束,您测试了
while (end - it > stride) 而不是while (it + stride < end) 。 - 我已经先发制人了,这是错误的。
- @编辑:是的,你是对的,抱歉。
- 先发制人,如中所述,我在你的评论上面展示了如何防止越界。另外,你的循环是错误的;它确实超出了界限(当
i + 2 == v.end() 时,你做i += 2 使i = v.end() 并测试v.end() + 2 <= v.end() ,因此是ub)。 - @对了,你的
end - it > stride 也错了,你只要用1 代替跨步,你的情况就相当于it + 1 < end ,这和平常的it < end 不一样。我想我想说的是(因为我以前确实遇到过这种情况,我只是不记得确切的代码,所以我错了)你会把它写为for (i = v.begin(); i <= v.end() - 2; i += 2) (假设你知道你总是有一个要处理的元素,这不是我的观点)。也就是说,你不能用< 来做,你需要一个<= 。 - @Benvoigt:所以如果你想在向量没有足够元素的情况下修复这个边缘情况,你的修复应该是
v.end() - i >= stride ,而不是v.end() - it > stride ,这与你的声明不同。换言之,当步幅大于1 时,严格的不平等已经是一个糟糕的选择,所以他们不会对我说的话提出任何问题。我只是在解释我之前的意思时犯了错,对不起。 - @梅尔达:这是正确的条件,测试需要在增量之前立即进行。你的还是错的。我想我可以写一篇关于这个的博文。正确合计为:
auto it = begin(), end = end(); if (it != end) { process(*it); while (stride < end - it) { it += stride; process(*it); } } 。 - @我不认为我的错。
v.end() - i >= stride 的哪一部分是错误的? - @迈赫达德:然后你在环体内尊重
v.end() 的部分。 - @本沃伊特:我不明白,我在哪里可以取消引用
v.end() ?完整代码为for (i = v.begin(); v.end() - i >= stride; i += stride) { foo(*i); } ,供参考。 - @好吧,现在你提前终止了一个迭代,当长度不可分割时。
- @不,我认为你还是错的。我没有改变任何东西,它和上面的条件完全一样,我只是把循环的其余部分加了回去。很肯定是对的。
- @迈尔达:从0数到10,步幅3…(正确的结果是0,3,6,9,但您的结果是0,3,6)
- @班沃伊特:哦--是的,你是对的,这让我终于明白了我为什么要写这篇文章:我把大跨步的场景和我们想把数组分成一定大小的块的场景混淆了……在这种情况下,9确实会被跳过,因为没有从9开始的3大小的完整间隔。但当你提到跨步时,我并没有意识到这两者之间的区别;这真是一个很好的技巧,谢谢!如果你写一篇博文,你应该同时提到这两个问题,因为很可能人们会犯我刚才犯的同样的错误。
- 啊,这就解释了。