我正在做一些概念概要证明并优化示例类型。但是,我遇到了一些我无法解释的事情,我希望有人可以解决这个问题。
我写了一段很短的代码:
int main (void)
{
for (int j = 0; j < 1000; j++)
{
a = 1;
b = 2;
c = 3;
for (int i = 0; i < 100000; i++)
{
callbackWasterOne();
callbackWasterTwo();
}
printf("Two a: %d, b: %d, c: %d.", a, b, c);
}
return 0;
}
void callbackWasterOne(void)
{
a = b * c;
}
void callbackWasterTwo(void)
{
b = a * c;
}
所有这一切都是调用两个非常基本的函数,只是将数字相乘。由于代码是相同的,我希望探查器(oprofile)返回大致相同的数字。
我为每个配置文件运行此代码10次,我得到以下值,表示每个函数花费的时间:
callbackWasterOne和callbackWasterTwo之间的时间差异非常大(至少对我而言),因为它们具有相同的代码,我在代码中切换了它们的顺序,并且现在用以下结果重新启动了分析器:
显然,探查器根据执行顺序采样比另一个更多。不好。无视这一点,我决定看到删除一些代码的效果,我得到了执行时间(平均值):
所以这就是我理解的问题:
我查看了反汇编,代码中的两个函数是相同的。对它们的呼叫是相同的,删除呼叫只会删除一个呼叫线路。
可能相关的其他信息
以下是我使用-O1
优化时的结果:
删除各种位的结果(执行时间平均值):
因此现在删除一个调用比以前好多了,删除它们仍然带来好处(可能是因为优化器理解循环中没有任何反应)。尽管如此,删除一个比我预期的更有益。
使用不同变量的两个函数的结果: 我为callbackWasterTwo()定义了另外3个变量,而不是重用相同的变量。现在结果是我所期望的。
删除各种位的结果(执行时间平均值):
所以现在删除两个调用与删除一个调用+另一个调用几乎相同(在stdev中)。 由于删除任一函数的结果几乎相同(43.83%对比44.07%),我将说明问题可能是剖析器数据(46%对42%)仍然存在偏差。也许这就是它的样本(接下来要改变计数器值,看看会发生什么)。
似乎优化的成功与代码重用分数有很大关系。实现&#34;完全&#34;的唯一方法(你知道我的意思)探查器指出的加速是优化完全独立的代码。无论如何,这一切都很有趣。
我仍然在寻找-O1
案例减少70%的一些解释提示......
我用10个函数(每个函数中有不同的公式,但是使用6个不同变量的一些组合,一次3个,所有乘法)来做到这一点:
至少可以说这些结果令人失望。我知道功能是相同的,但是,分析器表明有些功能需要更长的时间。无论我删除哪一个(&#34;快速&#34;或&#34;慢&#34;一个),结果都是一样的;)所以这让我想知道,有多少人错误地依赖于探查器指出要修复的错误代码区域?如果我在不知不觉中看到了这些结果,有什么可能告诉我去修复5%的功能而不是20%(即使它们完全一样)?如果5%的一个更容易修复,具有很大的潜在利益怎么办?当然,这个分析器可能不是很好,但它很受欢迎!人们用它!
这是截图。我不想再次输入它:
我的结论:我对Oprofile感到非常失望。我决定通过命令行在同一个函数上尝试callgrind(valgrind),它给了我更多合理的结果。实际上,结果非常合理(所有函数花费的时间相同 - 执行时间相同)。我认为Callgrind的样本远远超过Oprofile。
Callgrind仍然无法解释删除函数时改进的差异,但至少它提供了正确的基线信息......
答案 0 :(得分:2)
-O1
中也可以很容易地减少这样的样板。
如果它真的只是缺少的调用,那么这可以解释时序差异 - -O0
堆栈操作代码中有很多样板(任何调用者保存的寄存器都必须被压入堆栈,并且任何参数也是如此,然后必须处理任何返回值并且必须完成相反的堆栈操作)这有助于调用函数所花费的时间,但不一定完全归因于函数本身{{1因为该代码是在实际调用函数之前/之后执行的。
我怀疑第二个函数似乎总是花费更少时间的原因是需要完成更少(或没有)堆栈杂耍 - 由于之前的函数调用,参数值已经在堆栈中,所以,正如您所见,只需要执行对函数的调用,而无需任何其他额外工作。