虽然Visual Studio的某些版本(如专业版)包含了性能分析器,但例如专业版用户则没有这样的功能。本文将介绍一种使用免费工具对C++应用程序进行性能分析的方法,并提供了一个简单的应用程序来以信息丰富的方式展示结果。
虽然最终的解决方案可能不如集成的性能分析器那样方便,但如果只在发现瓶颈时使用性能分析,这种方法肯定是足够的——而且可以节省5000欧元。
以下方法已在Visual Studio2008专业版和VisualC++2010 Express中进行了测试。
这种方法包括四个步骤:
注意:以下步骤当然应该使用发布构建,并启用调试符号。
在Visual Studio2008中,转到解决方案资源管理器,选择项目并打开属性页面。在"配置属性"部分,选择"链接器" - "高级"。
将"Profile"设置为"启用分析信息(/PROFILE)"(其他版本的Visual Studio的确切步骤可能有所不同)。
编译项目。
首先,安装Visual Studio性能工具。可以在以下链接找到Visual Studio 2008 Service Pack 1独立性能分析器。
生成报告需要在命令行中输入一些命令。
打开命令提示符。定义性能工具的快捷方式并切换到应用程序所在的文件夹:
set pt="C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Performance Tools"
cd [my_app_folder]
最简单的方法是对应用程序的整个运行时间进行性能分析。
:: 启动性能分析器
%pt%\vsperfcmd /start:sample /output:my_sampled_data.vsp
:: 通过性能分析器启动应用程序
%pt%\vsperfcmd /launch:my_app.exe
:: 关闭性能分析器(此命令等待,直到应用程序终止)
%pt%\vsperfcmd /shutdown
也可以只对应用程序运行时间的一部分进行性能分析。
这里的处理变得有点繁琐,所以认为最好实现一些简单的应用程序来自动化以下步骤。无论如何,从未真正需要只对运行时间的一部分进行性能分析,因为可以通过修改应用程序的启动代码,总是能够找到代码,它花费90%的时间在相关部分。尽管如此,如果不能适当地修改代码,这里有额外的步骤。
:: 如上所述启动vsperfcmd,然后将其附加到应用程序:
%pt%\vsperfcmd /start:sample /output:my_sampled_data.vsp
:: 将性能分析器发送到等待状态
%pt%\vsperfcmd /globaloff
:: 通过指定pid将性能分析器附加到应用程序
%pt%\vsperfcmd /attach:[pid]
附加到应用程序后,执行任何必要的准备工作。现在启动想要性能分析的函数并激活性能分析器。
:: 激活性能分析器
%pt%\vsperfcmd /globalon
当函数完成(或者认为已经过去足够的时间)时,停止性能分析:
:: 从应用程序中分离性能分析器
%pt%\vsperfcmd /detach
:: 关闭性能分析器
%pt%\vsperfcmd /shutdown
工具vsperfreport加载符号文件(.pdb)并将它们与生成的报告(.vsp)合并,创建.csv文件。
:: 生成报告文件(.csv)
%pt%\vsperfreport /summary:all my_sampled_data.vsp
如果也对系统文件中的调用感兴趣,也可以指定符号服务器或本地缓存的路径。
%pt%\vsperfreport /summary:all my_sampled_data.vsp
/symbolpath:"srv*C:\MySymbolCache*http://msdl.microsoft.com/download/symbols"
上述csv文件原则上可以用任何电子表格应用程序打开,尽管它们通常很大,并且不立即有用。
因此,编写了一个小型应用程序,它解析生成的文件之一(名为"...CallerCalleeSummary.csv"的文件),并以按总百分比排序的树状视图呈现它。从这个视图开始,应该很容易找到性能问题的根源。就而言,可以在C++遗留代码中发现几个瓶颈,仅仅通过查看代码是永远找不到的。
还附带了一个小型C++应用程序,为其生成了性能分析。生成的.vsp和.csv文件包含在"Release"子文件夹中。使用ProfileResultViewer打开"my_sampled_data_CallerCalleeSummary.csv"应该类似于上面给出的屏幕截图。
性能分析报告将仅显示函数的统计信息,而不是单独的行。
内联函数不会出现在性能分析中,因为它们也从未出现在本地调用堆栈中。
呈现的树状视图不是严格的层次结构:如果A调用B,那么在呈现的视图中,B可能看起来需要比A更多的时间。这是因为显示的是总百分比,即,它不显示B在从A调用时花费了多少时间,而是显示B总共花费了多少时间。不确定csv文件是否包含足够的信息来恢复更详细的数字。
当然,上述一些步骤可以很容易地集成到一些GUI应用程序中,但目前描述的程序对于来说是足够简单的。