我怎样才能获得更好的分析?

时间:2009-03-07 15:48:34

标签: c++ c macos gcc profiling

我需要分析一个程序,看看是否需要对性能进行任何更改。我怀疑有需要,但先测量是要走的路。这不是那个程序,但它说明了我遇到的问题:

#include <stdio.h>
int main (int argc, char** argv)
{
  FILE* fp = fopen ("trivial.c", "r");
  if (fp)
  {
    char line[80];
    while (fgets (line, 80, fp))
      printf (line);

    fclose (fp);
  }
  return 0;
}

以下是我用它做的事情:

% gcc trivial.c -pg -o trivial
% ./trivial
...
% gprof trivial gmon.out

当然,这是一个简单的程序,但我认为它会在分析雷达上产生某种影响。它没有:

                                  called/total       parents 
index  %time    self descendents  called+self    name       index
                                  called/total       children

                0.00        0.00       1/1           __start [1704]
[105]    0.0    0.00        0.00       1         _main [105]

-----------------------------------------------

  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
  0.0       0.00     0.00        1     0.00     0.00  _main [105]

Index by function name

 [105] _main              

任何人都可以在这里指导我吗?我希望输出反映它至少14次调用fgets和printf,并且它确实击中了磁盘 - 肯定会有一些测量时间。

当我在真实程序上运行相同的命令时,我会列出更多的功能,但即使这样,它也不是一个完整的列表 - 只是一个样本。

也许gprof不是正确的工具。是什么?

这是在OS X Leopard上。

编辑:我运行了真正的程序并得到了这个:

% time real_program
real      4m24.107s
user      2m34.630s
sys       0m38.716s

9 个答案:

答案 0 :(得分:5)

我认为您可以尝试使用各种Valgrind工具,尤其是callgrind(用于获取程序中发生的每次通话的通话计数和包含成本)。

valgrind输出有各种不错的可视化工具。我不知道OS X的特定工具。

答案 1 :(得分:5)

默认情况下,gprof会显示有限的数据。这很好。看看你的输出 - 它只提到main(这是默认值)。现在,查看calls列 - 这就是您想要的。但是对于其他功能,请尝试:

gprof -e main -f printf -f fgets trivial > gprof.output

这是一些命令的link。另外,在您的系统上尝试man gprofHere是如何解释数据的。

此外,查看ltracestraceptrace(如果可用 - 我再也不记得是否所有这些都在OSX上) - 它们很有趣!< / p>

答案 2 :(得分:3)

Shark是开发人员工具中包含的分析器。

答案 3 :(得分:3)

在分析代码之前,您需要查看程序花​​费时间的位置。在时间(1)下运行它以查看相应的用户,系统和挂钟时间。只有当用户时间接近挂钟时间时,才能对代码进行概要分析。如果用户和系统时间与挂钟时间相比非常小,那么您的程序是I / O绑定的;如果系统时间接近挂钟时间,则程序是内核绑定的。在这两种情况下,在strace -c或合适的dtrace脚本下运行程序,以确定每次系统调用所花费的时间。

答案 4 :(得分:2)

分析并不表示磁盘访问,只是调用了哪些函数,而且由于VM缓存,这些函数不具代表性。

Valgrind doesn't work well on OS X.

使用Leopard,您拥有Dtrace实用程序;我没有使用它,但它可能会为您提供您正在寻找的信息。

答案 5 :(得分:1)

缺少某些功能通常意味着不会编译这些功能以进行性能分析。具体来说,要分析使用标准函数的代码,例如printf(几乎总是,我猜),您需要一个使用分析支持编译的C库版本。我不熟悉OS X,但在Linux上我需要安装包含libc_p库的libc6-prof软件包。

B.t.w。,我相信OS X(或者可能是XCode?)附带了一个分析工具。它不像gprof方法那样精确,因为它使用了采样,但您可以在任何程序上运行它而无需特殊编译。

答案 6 :(得分:1)

看一下您的程序,因为您正在使用文件处理(仅限),它还取决于所启用的任何缓存。因此,请注意,您的分析结果可能会因您的缓存行为而异。

答案 7 :(得分:0)

在这项业务中有一些普遍接受的信念,我建议你仔细研究。

一个是找到性能问题的最佳方法(如果不是唯一的方法)是测量每个子程序的时间并计算它被调用的次数。

这是自上而下的。它源于一种信仰,即森林比树木更重要。它基于“代码速度”和“瓶颈”的神话。它不是很科学。

性能问题更像是一个bug而不是一个定量的东西。错误的是浪费时间,需要修复它。它基于一个简单的观察:

缓慢包含因不良原因而花费的时间。

要找到它,请在随机时间片段中对程序状态进行采样,并调查其原因。

如果某些事情导致缓慢,那么这个事实就会将它暴露给你的样本。因此,如果你采取足够的样本,你会看到它。通过显示它的样本分数,您将知道花费多少时间。

判断一小段时间是否有充分理由的好方法是仔细查看调用堆栈。堆栈上的每个函数调用都有一个隐含的原因,如果这些原因中的任何一个都很差,那么整个样本的原因就很差。

一些分析师在声明级别告诉您每个陈述对您造成的损失。

就个人而言,我只是随机地停止了几次。出现在多个样本上的任何调用都可能是怀疑的候选者。它永远不会失败。

你可能会说“这不准确”。这非常准确。它精确地指出了导致问题的指令。它没有给你3个小数位的定时精度。即它对测量来说很糟糕,但对于诊断来说却很棒。

您可能会说“递归怎么样?”。那么,它呢?

你可能会说“我认为这只适用于玩具程序。”那只是希望。事实上,大型程序往往会有更多的性能问题,因为它们有更深的堆栈,因此有更多机会进行调用,原因很糟糕,并且采样发现它们很好,谢谢。

很抱歉成为一名痴迷者。我只是讨厌在一个基于科学的领域看到神话。

MORE

答案 8 :(得分:0)

对于上面给出的示例代码,如果您对调用堆栈进行了多次采样,您将基本上看到这些堆栈:

-------------------------------------
...
main.c:  4 call _fopen
...        call _main
-------------------------------------
...
main.c:  8 call _fgets
...        call _main
-------------------------------------
...
main.c:  9 call _printf
...        call _main
-------------------------------------
...
main.c: 11 call _fclose
...        call _main

并且比例将告诉您大致在每次通话中花费的时间。您不太可能看到其他因为与I / O库调用相比,“独占”时间基本上为零。这就是堆栈样本可以告诉你的内容 - 无论程序有多大,精确的陈述都会花费你最多,大致花费多少。