我最近在处理一段代码,其中性能非常重要,基本上我有以下情况:
int len = some_very_big_number;
int counter = some_rather_small_number;
for( int i = len; i >= 0; --i ){
while( counter > 0 && costly other stuff here ){
/* do stuff */
--counter;
}
/* do more stuff */
}
所以这里我有一个经常运行的循环,并且对于一定数量的运行,while块也将被执行,直到变量counter
减少到零,然后while循环将不被调用,因为第一个表达式将为false。
现在的问题是,如果使用
之间的性能存在差异
counter > 0
和counter != 0
?
我怀疑会有,有没有人知道这方面的细节。
答案 0 :(得分:34)
要衡量就是知道。
答案 1 :(得分:19)
你认为什么会解决你的问题! :d
if(x >= 0)
00CA1011 cmp dword ptr [esp],0
00CA1015 jl main+2Ch (0CA102Ch) <----
...
if(x != 0)
00CA1026 cmp dword ptr [esp],0
00CA102A je main+3Bh (0CA103Bh) <----
答案 2 :(得分:19)
在编程中,以下陈述是指示地狱之路的标志:
我最近在编写一段代码,其中性能非常重要
以最干净,最易懂的方式编写代码。周期。
完成后,您可以测量其运行时间。如果花费太长时间,衡量瓶颈,并加速最大的瓶颈。继续这样做,直到它足够快。
由于误导强调盲目优化而失败或遭受灾难性损失的项目清单是巨大而悲惨的。不要加入他们。
答案 3 :(得分:8)
我认为你花时间优化错误的东西。 “这里代价高昂的其他东西”,“做东西”和“做更多东西”更重要的是要看。这就是我打赌会带来巨大性能改进的地方。
答案 4 :(得分:4)
如果计数器以负数开头,则会出现巨大的差异。否则,在我熟悉的每个平台上,都没有区别。
答案 5 :(得分:3)
counter > 0
和counter != 0
之间有区别吗?这取决于平台。
一种非常常见的CPU类型是我们在PC中使用的英特尔CPU。两个比较都将映射到该CPU上的单个指令,并且我假设它们将以相同的速度执行。但是,要确定您必须执行自己的基准测试。
答案 6 :(得分:3)
正如吉姆所说,如果有疑问,请亲自看看:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
using namespace boost::posix_time;
using namespace std;
void main()
{
ptime Before = microsec_clock::universal_time(); // UTC NOW
// do stuff here
ptime After = microsec_clock::universal_time(); // UTC NOW
time_duration delta_t = After - Before; // How much time has passed?
cout << delta_t.total_seconds() << endl; // how much seconds total?
cout << delta_t.fractional_seconds() << endl; // how much microseconds total?
}
这是衡量时间的一种非常好的方式。希望有所帮助。
答案 7 :(得分:2)
认为比较的类型会在不知情的情况下产生影响,是猜测的定义。
不要猜。
答案 8 :(得分:1)
好的,你可以测量一下,当然。但是,这些类型的比较速度如此之快,以至于您可能会在这一行代码上看到基于处理器交换和调度的更多变化。
这种不必要的,过早的优化气味。正确的你的程序,优化你看到的。如果您需要更多,个人资料,然后从那里开始。
答案 9 :(得分:1)
我想补充一点,这个代码在现代cpus上的压倒性的性能方面将不是由比较指令主导,而是比较是否很好地预测因为任何错误预测会比任何整体操作浪费更多的周期。
因此,循环展开可能是最大的赢家,但衡量,衡量,衡量。
答案 10 :(得分:0)
通常,它们应该是等效的(两者通常在单周期指令/微操作中实现)。您的编译器可能会执行一些奇怪的特殊情况优化,这很难从源级别进行推理,这可能会使一个稍微快一点。此外,平等测试比不等式测试(&gt;)更节能,尽管系统级效应非常小,不值得讨论。
答案 11 :(得分:0)
可能没有区别。您可以尝试检查每个组件的输出。
话虽如此,判断任何差异是否显着的唯一方法是尝试两种方式和衡量标准。我敢打赌,这种改变对优化没有任何影响。
答案 12 :(得分:0)
假设您正在为x86架构开发,当您查看程序集输出时,它将归结为jns vs jne。 jns将检查符号标志,jne将检查零标志。据我所知,这两项行动同样代价高昂。
答案 13 :(得分:0)
显然,解决方案是使用正确的数据类型。
使计数器成为无符号整数。然后它不能小于零。您的编译器显然会知道这一点,并被迫选择最佳解决方案。
或者你可以测量它。
您还可以考虑如何实施......(这里我们进行切线)......
当然,计算机很有趣,检查单个位可能比整个值花费更长时间(无论平台上有多少字节)。
你可以测量它......
你可以发现它比另一个更优化(在你测量它的条件下)。但是你的程序仍然会像狗一样运行,因为你花了所有的时间来优化代码的错误部分。
最好的解决方案是使用许多大型软件公司所做的事情 - 将硬件归咎于没有足够快速运行并鼓励您的客户升级他们的设备(由于您的产品运行速度不够快,这显然是劣质的)。
&LT; /咆哮&GT;
答案 14 :(得分:0)
我刚才遇到了这个问题,问了3年之后,所以我不确定答案仍然有多么有用......不过,我很惊讶没有看到明确表示回答你的问题需要知道只有两件事:
首先,每个处理器都有不同的测试指令。在一个给定的处理器上,两个类似的比较可能会变为采用不同数量的周期。例如,您可能有一个1周期的指令来执行gt(&gt;),eq(==)或le(&lt; =),但没有1周期指令用于其他比较,例如ge(&gt; =)。在测试之后,您可能决定执行条件指令,或者更常见的是,如在代码示例中那样,跳转。同样,跳转所花费的周期在大多数高端处理器上采用可变数量的周期,这取决于是否采用,预测或未预测条件跳转。当您在汇编中编写代码并且您的代码对时间至关重要时,您实际上可能需要花费大量时间来确定如何最佳地安排代码以最小化整体循环计数,并最终可能需要优化的解决方案基于给定比较返回真或假的时间。
这引出了我的第二点:编译器,如人类编码器,尝试安排代码以考虑可用的指令及其延迟。他们的工作比较困难,因为汇编代码所知的一些假设就像“计数器很小”一样很难(不是不可能)知道。对于像循环计数器这样的琐碎案例,大多数现代编译器至少可以识别出计数器将始终为正,并且a!=将与&gt;相同。从而产生最佳代码。但是,正如帖子中提到的那样,你只会知道你是运行测量,还是检查你的汇编代码并说服自己这是你在装配中可以做的最好的。当您升级到新的编译器时,您可能会得到不同的答案。