我需要分析一个程序,看看是否需要对性能进行任何更改。我怀疑有需要,但先测量是要走的路。这不是那个程序,但它说明了我遇到的问题:
#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
答案 0 :(得分:5)
我认为您可以尝试使用各种Valgrind工具,尤其是callgrind
(用于获取程序中发生的每次通话的通话计数和包含成本)。
valgrind输出有各种不错的可视化工具。我不知道OS X的特定工具。
答案 1 :(得分:5)
默认情况下,gprof
会显示有限的数据。这很好。看看你的输出 - 它只提到main(这是默认值)。现在,查看calls
列 - 这就是您想要的。但是对于其他功能,请尝试:
gprof -e main -f printf -f fgets trivial > gprof.output
这是一些命令的link。另外,在您的系统上尝试man gprof
。 Here是如何解释数据的。
此外,查看ltrace
,strace
和ptrace
(如果可用 - 我再也不记得是否所有这些都在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个小数位的定时精度。即它对测量来说很糟糕,但对于诊断来说却很棒。
您可能会说“递归怎么样?”。那么,它呢?
你可能会说“我认为这只适用于玩具程序。”那只是希望。事实上,大型程序往往会有更多的性能问题,因为它们有更深的堆栈,因此有更多机会进行调用,原因很糟糕,并且采样发现它们很好,谢谢。
很抱歉成为一名痴迷者。我只是讨厌在一个基于科学的领域看到神话。
答案 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库调用相比,“独占”时间基本上为零。这就是堆栈样本可以告诉你的内容 - 无论程序有多大,精确的陈述都会花费你最多,大致花费多少。