我最近尝试使用OProfile分析我的应用程序。收集的数据对我来说已经非常有价值,但我对它的精确解释有困难。运行我的应用程序并设置并运行oprofile之后,我生成了报告并获得了:
root @ se7xeon:src#opreport image:test -l -t 1
溢出统计数据不可用 CPU:P4 / Xeon,带2个超线程,速度3191.66 MHz(估计)
计算GLOBAL_POWER_EVENTS事件(处理器未停止的时间),单位掩码为0x01(强制)计数750000
样品%符号名称
215522 84.9954 cci :: Image :: interpolate(unsigned char *,cci :: Matrix const&)const
17998 7.0979 cci :: Calc :: diff(unsigned char const *,unsigned char const *)
13171 5.1942 cci :: Image :: getIRect(unsigned char *,int,int)const
5519 2.1765 cci :: Image :: getFRect(unsigned char *,double,double)const
好的,所以我的插值函数负责84%的应用程序(太长)执行时间。看起来似乎是一个好主意:
root @ se7xeon:src#opannotate image:test --source
[...]
/* cci::Image::interpolate(unsigned char*, cci::Matrix<cci::Point2DF> const&) const total: 215522 84.9954 */
1392 0.5529 :void Image::interpolate(CCIPixel *output, const Matrix<Point2DF> &inputPoints) const throw()
4 0.0016 :{
[...]
: col0 = static_cast<int>(point[idx].x);
3 0.0012 : col1 = col0+1;
629 0.2498 : row0 = static_cast<int>(point[idx].y);
385 0.1529 : row1 = row0+1;
56214 22.3266 : if (col0 < 0 || col1 >= m_width || row0 < 0 || row1 >= m_height)
: {
: col0 = row0 = col1 = row1 = 0;
: }
如果我理解正确,if条件是程序执行时间的22%以上。开括号和函数声明似乎需要时间,是否应该对应于函数调用开销(“在堆栈,跳转,弹出参数上推送参数”序列)?
我改变了源代码中的一些东西(与后来的瓶颈相关,因为我不知道如何优化if),重新编译,再次运行oprofile(不要忘记opcontrol --reset)。现在带注释的代码在同一个地方看起来像这样:
6 0.0024 : curPx = point[idx].x;
628 0.2477 : curPy = point[idx].y;
410 0.1617 : col0 = static_cast<int>(curPx);
57910 22.8380 : col1 = col0+1;
: row0 = static_cast<int>(curPy);
: row1 = row0+1;
: if (col0 < 0 || col1 >= m_width || row0 < 0 || row1 >= m_height)
: {
: col0 = row0 = col1 = row1 = 0;
: }
这次if基本上没有时间(?),最昂贵的指令是“col1 = col0 + 1”,整个计时块似乎已向上移动。怎么会这样?这根本可以信任来查明来源中的瓶颈吗?
另一个让我怀疑的是,当我设置opcontrol时,我以GLOBAL_POWER_EVENTS的形式输入了跟踪事件,样本数为750k。在输出中,插值函数似乎占84%,但其中记录的样本数量仅略高于200k。这甚至不是所要求数量的50%。我是否理解剩余的~500k样本是由未在输出中列出的应用程序(内核,Xorg等)获取的?
答案 0 :(得分:3)
在分析优化代码时,您实际上无法依赖准确的源代码行。编译器移动的东西太多了。
要获得准确的图片,您需要查看代码反汇编程序输出。
答案 1 :(得分:2)
OProfile可以(他们告诉我)在挂钟时间(而不是CPU)上获取堆栈样本,它可以为您提供行级百分比。您正在寻找的是包含在大量堆栈样本中的行。
在我完成手动调整代码之前,我不打开编译器优化,因为它只是隐藏了一些东西。
当你说插值例程使用84%的时间时,会触发一个问题。整个程序需要一些时间,对吗?这需要100%的时间。如果你将程序的时间缩短一半,或者你把它加倍,它仍然需要100%的时间。插值的84%是否过多取决于它是否超出必要的范围。
所以我建议你不要问一个例程的百分比是否过多。相反,你会寻找占用大量时间的代码行,并询问它们是否可以进行优化。看到不同?在优化代码之后,它可以大大减少总体运行时间,但它可能仍然是一个很大的百分比,总数较小。 当没有什么需要很大的代码时,代码不是最佳的。 代码是最佳的,当所有事情占很大比例时,没有一个可以改进。
我不关心只提供数字的事情。我想要的是洞察力。例如,如果该例程占84%的时间,那么如果您使用10 samples of the stack,那么它将是8.4。确切的数字并不重要。重要的是要理解为什么它在那里。真的有必要在那里吗?这就是看堆栈样本可以告诉你的。也许你实际上在必要时经常进行插值两次?人们常常通过分析为什么来发现,他们试图加速的例程并不需要被调用,也许根本不需要。在你的情况下,我无法猜测。只有检查程序状态的见解才能告诉你。