使用GProf来优化你的C/C++程序 |
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 代码。