最近要对推送程序进行性能优化,找出程序的hot spots,程序是用VS2005,C++写的,所以直接使用VS2005自带的性能分析工具对程序做了一次profiling。
准备工作
使用VS2005打开工程,在菜单“工具”下面有个“性能工具”的选项,点击右边的“性能向导”就可以开始新建一个性能测试项了。 如: 如果点击菜单没有看到上图的“性能工具”选项,说明安装时候没有选择安装测试工具,请到“控制面板”->“添加删除程序”里面进入VS2005的维护模式,将“team development和team test工具”选上。如图: 重新安装VS2005,重启VS2005即可。
测试模式
有两种:采样和检测。
对于一个Profiler来说,给程序带来负担(overhead)是难以避免的。一般来说,Profiler都会提供多种探测方式,例如采样(Sampling)或是追踪(Tracing)。“采样”一般都是定时打点,看看程序每个线程当前落在哪个方法里,当前的调用堆栈是什么,以此发现程序热点,“追踪”则往往会记录方法进入和退出的时间,所以显然前者的负担要小过后者。赵人希的微信
“检测”应该就是赵人希所说的“追踪”方式。本次测试开始用“采样”方式,但是运行时报错,所以最终使用了“检测”方式。
测试过程
通过向导非常简单的建立了测试项之后就会出现“性能资源管理器”。设好参数,点击开始。程序就跑起来了。这跟普通的调试程序编译运行没啥两样。在此过程中,测试程序不断的给被测程序发请求,增加压力,以便较好的模拟程序真实的使用场景。压了10多分钟吧,估计差不多了,退出被测程序,VS就开始生成性能分析报告,此过程甚是漫长,估计快弄了20分钟左右。看来我们的推送程序还是做了不少工作。接下去就可以看到分析结果了。
测试结果
对于测试结果的查看可以参考MSDN上的介绍。 具体到本次测试程序,结果如下: 从最多调用次数看:由于函数名都是C++链接之后的名字,函数名部分被改写无法识别,猜测前两个是基本的stl的字符串比较和赋值等操作,第三个是memset,调用次数最多但不一定是最耗时的瓶颈。 耗时最长分别为wait(ACE线程等待函数)、伊诺收包的回调函数OnRecvRequest和伊诺的定时器OnTimer函数。估计wait函数是阻塞的,一直都在wait不耗CPU?反正这个目前也不在我的控制范围之内,先不管;伊诺的OnRecvRequest和OnTimer函数是直接调用的TCClient.dll。无代码也不能控制。囧oz。 接下去的几个tab主要展示的几列为:总调用次数、函数本身调用所用时间(不包括函数中调用其他函数的时间。只是本身的耗时)简称“净时间”、略、函数调用所用的时间(包括函数调用中调用其他函数的时间)简称“总时间”、略 通过净时间和总时间的关系、函数调用树的关系可以找到程序的热点——哪个函数比较耗时。 首先,耗时的函数其总时间与净时间之差应该很小,一眼看去应该是基本相等的。 其次,对总时间排序,通过函数调用关系树,逐级查看,最终找到合适粒度的耗时函数。 memset的调用最多。与代码中把C++当C用、满眼所见都是memset十分吻合。但也可以看出memset虽然用的很多,但由于其效率很高,总的耗费时间并不高。
ACE队里的dequeue函数在队列为空时会阻塞。通过函数调用关系tree发现最终调用花费的时间都用在了wait函数上。 以Process函数为例,逐级查看下去,最耗时的操作在函数MsgReqProc1中,其最耗时的操作均为数据库操作。如图
通过对总时间高的多个函数的追踪,综合查看可以发现,此程序最耗时的操作均是对数据库上的操作。后面对数据库的分析也印证了此结论。
从上面图中可以看出,所有涉及到数据库操作的耗时都是实打实的。而且增删查改的操作都是耗时的大户。能少用尽量少用。而且ODBCDB.dll的总的净耗时比PubServ.dll的耗时高很多。也说明了其消耗了大量的时间。
优化方法
- 减少不必要的数据库操作。
- 对数据库采用异步、批量的操作方式,减少等待和阻塞。
- 多使用缓存。
- 优化数据库性能。如采用内存数据库、优化存储过程性能等。