我该如何解释OProfile输出?

时间:2010-10-27 14:47:33

标签: c++ linux profiling oprofile

我最近尝试使用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等)获取的?

2 个答案:

答案 0 :(得分:3)

在分析优化代码时,您实际上无法依赖准确的源代码行。编译器移动的东西太多了。

要获得准确的图片,您需要查看代码反汇编程序输出。

答案 1 :(得分:2)

OProfile可以(他们告诉我)在挂钟时间(而不是CPU)上获取堆栈样本,它可以为您提供行级百分比。您正在寻找的是包含在大量堆栈样本中的行。

在我完成手动调整代码之前,我不打开编译器优化,因为它只是隐藏了一些东西。

当你说插值例程使用84%的时间时,会触发一个问题。整个程序需要一些时间,对吗?这需要100%的时间。如果你将程序的时间缩短一半,或者你把它加倍,它仍然需要100%的时间。插值的84%是否过多取决于它是否超出必要的范围。

所以我建议你不要问一个例程的百分比是否过多。相反,你会寻找占用大量时间的代码行,并询问它们是否可以进行优化。看到不同?在优化代码之后,它可以大大减少总体运行时间,但它可能仍然是一个很大的百分比,总数较小。 当没有什么需要很大的代码时,代码不是最佳的。 代码是最佳的,当所有事情占很大比例时,没有一个可以改进。

我不关心只提供数字的事情。我想要的是洞察力。例如,如果该例程占84%的时间,那么如果您使用10 samples of the stack,那么它将是8.4。确切的数字并不重要。重要的是要理解为什么它在那里。真的有必要在那里吗?这就是看堆栈样本可以告诉你的。也许你实际上在必要时经常进行插值两次?人们常常通过分析为什么来发现,他们试图加速的例程并不需要被调用,也许根本不需要。在你的情况下,我无法猜测。只有检查程序状态的见解才能告诉你。