2006年5月22日星期一

实战valgrind来优化程序

前一阵子写了一个gzip的解压程序,因为是用在嵌入式环境,而且是用于demo的目的,所以就一味的压缩空间,没有太多考虑优化性能。最终实现的程序目标代码8K左右,解压一个2M的gzip文件,我的程序myzip和系统自带的zcat性能如下:

myzip: real 0m0.439s user 0m0.408s sys 0m0.008s

zcat: real 0m0.195s user 0m0.148s sys 0m0.028s


myzip的性能瓶颈一个是读取bit流的函数,另一个是huffman解码。出于节省空间的考虑,huffman树的设计相当紧凑,解码性能的优化空间不大,于是想对第一个瓶颈做点工作。在优化开始之前,先用valgrind侦察一下敌情。


首先把程序用-g参数重新编译。然后先看看程序有没有什么bug,按如下方式运行:


$valgrind --tool=memcheck ./myzip file.gz


运行结束时,valgrind打印如下信息(stripped):

==5111== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 11 from 1)

==5111== malloc/free: in use at exit: 0 bytes in 0 blocks.

==5111== malloc/free: 537 allocs, 537 frees, 239,144 bytes allocated.


恩,看来内存操作方面没啥问题,那接着profile一下:


$valgrind --tool=cachegrind ./myzip file.gz


运行完毕后,valgrind打印出程序运行的一些统计信息,并在当前目录生成一个文件cachegrind.out.xxxx。(xxxx是4个“随机"数字)。虽然这个文件是human readable的,但是有更好的工具来看 -kcachegrind。


用kcachegrind打开这个文件,窗口显示如下:



果不其然,读取bit流的函数get_bits占用了66.37%的cycles,huffman解码函数get_code占用了14.75%。那下面就拿get_bits开刀。

$%#^*&^*&,搞定。


再profile一下,kcachegrind的截图如下:



get_bits的比例下降到45.78%,get_code上升到22.64%。

再用-O3编译的程序解压上面的压缩文件结果如下:

real 0m0.361s user 0m0.340s sys 0m0.004s


总结:

本文的情况是作者非常了解程序的瓶颈,在大部分情况下,这种信息并不明显(特别是如果程序不是自己写的)。通过valgrind的cachegrind功 能,可以直观的得到这些信息。这些定量的信息,对于优化程序很有帮助。即使是对于本文所列的情况,作者知道这两个瓶颈,但要分析出这两个瓶颈哪个更堵,以 及优化后可能达到的最佳效果,valgrind提供的信息仍然很有价值。


没有评论: