original in en Arnout Engelen
en to CN 小 汪
Arnout Engelen 是荷兰Nijmegen大学计算机系的学生,也是网络安全公司TUNIX的雇员。在空闲时间,他喜欢长跑和吹奏高音萨克斯。
GProf 使用了一种异常简单但是非常有效的方法来优化C/C++ 程序,而且能很容易的识别出值得优化的代码。一个简单的案例分析将会显示,GProf如何通过识别并优化两个关键的数据结构,将实际应用中的程序从3分钟的运行时优化到5秒的。
这个程序最早可以追溯到1982年关于编译器构建的特别讨论大会(the SIGPLAN Symposium on Compiler Construction)。现在这个程序成了各种UNIX 平台上的一个标准工具。
event2dot
,一个将路径“事件”描述文件转化为图形化“dot”文件的工具(executable which translates a pathalizer 'events' file to a graphviz 'dot' file)。
简单的说,它从一个文件里面读取各种事件,然后将它们分别保存为图像(以页为节点,且将页与页之间的转变作为边),然后将这些图像整合为一张大的图形,并保存为图形化的'dot'格式文件。
event2dot
并用源码里的例子作为输入(大概55000的数据),大致要三分多钟:
real 3m36.316s user 0m55.590s sys 0m1.070s
g++ -pg dotgen.cpp readfile.cpp main.cpp graph.cpp config.cpp -o event2dot
现在我们可以再次运行event2dot
,并使用我们前面使用的测试数据。这次我们运行的时候,event2dot
运行的分析数据会被搜集并保存在'gmon.out'文件中,我们可以通过运行'gprof event2dot
| less'来查看结果。
gprof 会显示出如下的函数比较重要:
% cumulative self self total time seconds seconds calls s/call s/call name 43.32 46.03 46.03 339952989 0.00 0.00 CompareNodes(Node *,Node *) 25.06 72.66 26.63 55000 0.00 0.00 getNode(char *,NodeListNode *&) 16.80 90.51 17.85 339433374 0.00 0.00 CompareEdges(Edge *,AnnotatedEdge *) 12.70 104.01 13.50 51987 0.00 0.00 addAnnotatedEdge(AnnotatedGraph *,Edge *) 1.98 106.11 2.10 51987 0.00 0.00 addEdge(Graph *,Node *,Node *) 0.07 106.18 0.07 1 0.07 0.07 FindTreshold(AnnotatedEdge *,int) 0.06 106.24 0.06 1 0.06 28.79 getGraphFromFile(char *,NodeListNode *&,Config *) 0.02 106.26 0.02 1 0.02 77.40 summarize(GraphListNode *,Config *) 0.00 106.26 0.00 55000 0.00 0.00 FixName(char *)可以看出,第一个函数比较重要: 程序里面绝大部分的运行时都被它给占据了。
CompareNodes
函数上,用 grep 查看一下则发现CompareNodes 只是被CompareEdges
调用了一次而已, 而CompareEdges则只被addAnnotatedEdge
调用——它们都出现在了上面的清单中。这儿就是我们应该做点优化的地方了吧!
我们注意到addAnnotatedEdge
遍历了一个链表。虽然链表是易于实现,但是却实在不是最好的数据类型。我们决定将链表 g->edges 用二叉树来代替: 这将会使得查找更快。
real 2m19.314s user 0m36.370s sys 0m0.940s
% cumulative self self total time seconds seconds calls s/call s/call name 87.01 25.25 25.25 55000 0.00 0.00 getNode(char *,NodeListNode *&) 10.65 28.34 3.09 51987 0.00 0.00 addEdge(Graph *,Node *,Node *)看起来以前占用大量运行时的函数现在已经不再是占用运行时的大头了!我们试一下再优化一下呢:用节点哈希表来取代节点树。
这次简直是个巨大的进步:
real 0m3.269s user 0m0.830s sys 0m0.090s
perl -d:DProf mycode.pl
来开始,并使用dprofpp
来查看并分析结果。如果你可以用gcj 来编译你的Java 程序,你也可以使用gprof,然而目前还只支持单线程的Java 代码。