性能分析是指观察和记录有关应用程序行为的度量的过程。常见的性能问题源于运行速度慢或低效的代码,或者导致内存浪费的代码。
大多数性能分析工具都可以归为:
采样式:采样式性能分析器通过获取运行应用程序的周期性快照(称为采样),记录每个时间间隔内的应用程序运行状态,包括正在执行的代码行。通常情况下,不会修改被测系统代码,而倾向于采用外部透视方法。
插装式:采用侵入式方案。
两者的主要区别在于,采样式分析器在运行任何代码时都会检查应用程序,包括对外部库的调用。而插装式分析器则只从插装代码收集数据。
Visual Studio的性能分析工具对托管和非托管应用程序均适用,但对象分配跟踪功能只对托管代码有效。
使用Visual Studio对应用程序进行性能分析主要有4个步骤:
创建性能会话,选择性能分析方法(CPU采样、插装、内存采样或并发)及其目标
使用Performance Explorer浏览和设置会话属性
发起会话,执行应用程序和性能分析器
审查性能报告中收集的数据。
创建实例应用程序:
1. 创建WidgetClass.cs文件,代码如下:
1 namespace DemoConsole 2 { 3 public class WidgetClass 4 { 5 private string _name; 6 public string Name 7 { 8 get { return _name; } 9 set { _name = value; } 10 } 11 12 private int _id; 13 public int Id 14 { 15 get { return _id; } 16 set { _id = value; } 17 } 18 public WidgetClass(int id, string name) 19 { 20 _id = id; 21 _name = name; 22 } 23 } 24 }
2. 创建WidgetValueType.cs文件,代码如下:
1 namespace DemoConsole 2 { 3 public struct WidgetValueType 4 { 5 private string _name; 6 public string Name 7 { 8 get { return _name; } 9 set { _name = value; } 10 } 11 12 private int _id; 13 public int Id 14 { 15 get { return _id; } 16 set { _id = value; } 17 } 18 public WidgetValueType(int id, string name) 19 { 20 _id = id; 21 _name = name; 22 } 23 } 24 }
3. 在Program.cs文件中添加代码如下:
1 using System.Collections; 2 3 namespace DemoConsole 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 ProcessClasses(2000000); 10 ProcessValueTypes(2000000); 11 } 12 13 public static void ProcessClasses(int count) 14 { 15 ArrayList widgets = new ArrayList(); 16 for (int i = 0; i < count; i++) 17 { 18 widgets.Add(new WidgetClass(i, "Test")); 19 } 20 string[] names = new string[count]; 21 for (int i = 0; i < count; i++) 22 { 23 names[i] = ((WidgetClass)widgets[i]).Name; 24 } 25 } 26 27 public static void ProcessValueTypes(int count) 28 { 29 ArrayList widgets = new ArrayList(); 30 for (int i = 0; i < count; i++) 31 { 32 widgets.Add(new WidgetValueType(i, "Test")); 33 } 34 string[] names = new string[count]; 35 for (int i = 0; i < count; i++) 36 { 37 names[i] = ((WidgetValueType)widgets[i]).Name; 38 } 39 } 40 } 41 }
在对应用程序进行性能分析之前,首先需要创建一个性能会话,利用Performance Wizard创建会话,在其中确定一系列最常用的设置,也可以创建一个空的性能会话,或以某个单元测试结果为基础创建性能会话。
使用Performance Wizard
在VS2013的菜单栏上点击ANALYZE-->Performance and Diagnostics打开会话,选择Performance Wizard复选框,点击Start按钮,打开Performance Wizard:
选择默认选项,下一步,选择要分析的应用程序,然后完成。
添加空的性能会话
通过ANALYZE| Profiler| New Performance Session创建一个空的性能会话。然后手动指定会话的分析模式、目标和设置等。
从单元测试中创建性能会话
在VS2010中可以通过单元测试创建性能会话,首先要执行该单元测试,然后再Test Results窗口中右击该测试在上下文菜单中选择Create Performance Session即可。
创建了性能会话之后,就可以通过Performance Explorer对其进行浏览,Performance Explorer可以用于配置和执行性能会话,以及浏览性能会话结果。
在Performance Explorer中有两个目录:Targets用于指定会话执行时所分析的应用程序对象。Reports用于列出当前会话每次执行时的结果。
无论使用哪种方式创建会话,都需要查看和修改其中的设置,右击会话名选择属性:
采样分析是对应用程序进行性能分析的轻量级方法,它会周期性地中断目标程序的执行,记录正在执行的代码并获取调用栈快照。采样结束后,分析报告中将包含函数调用次数之类的数据,可以利用这些数据确定可能成为应用程序瓶颈或关键路径的函数,然后创建一个插装式会话分析对这些区域进行分析。
插装是在目标码中插入探针或标记的过程,当正常的程序流经这些插装点时,应用程序在该点的相关数据会被记录下来。插装会很快生成大量数据,因此应该先用采样模式找出潜在的问题域或热点,再根据采样结果对需要进一步分析的特定代码域进行插装。
流逝时间(Elapsed time)是指某个函数从开始运行到结束所花费的时间。应用程序时间(Application time)是指代码实际执行时间扣除系统事件处理时间之后的估计值。如果应用程序在性能分析过程中被另一个应用程序打断,那么流逝时间还包括执行这个应用程序所花费的时间,而应用程序时间则不包含这部分时间。包含时间(Inclusive time)包括当前函数的执行时间及它所调用的函数的执行时间。独占时间不包括被调用函数的执行时间。
单击Hot path中一个方法可转到函数的详细信息页面:
由于此例过于简单,因此并未给出过多有用信息。
在顶端Current View下拉列表里选择Functions切换到Functions视图,在该视图中能够看到一些感兴趣的结果:
在上面的视图中列出了会话期间被采样或插装的所有函数。对于插装式分析,该视图显示的是目标中被插装且会话期间能够被调用的所有函数。对于采样式分析,该视图将包含应用程序访问的所有其他成员/程序集,此例只有采样式分析,因此只包含后者。
下图为插装式分析样图:
Visual Studio抽象出一些实用工具来完成性能分析工作。如果需要更多的控制或者需要将性能分析集成在自动化批处理过程中,那么就可以直接使用这些实用工具。一般流程如下:
配置目标(必要时)和环境
启动数据库记录引擎
运行目标应用程序
应用程序运行结束时,停止数据记录引擎
生成会话报告
使用Visual Studio可以对Javascript进行性能分析,首先要设置一个插装式会话:
执行此性能分析会话时,性能分析器在收集应用程序的函数调用信息的同时也会收集JavaScript函数的性能信息。
常见的性能分析问题之调试符号
在查看性能分析报告时可以发现,一旦函数调用被解析到了对我们没有任何帮助的实体,如[ntdll.dll],当应用程序使用了无法找到调试符号的代码时,常常会发生这种情况,因此,我们得到的只是包含它的二进制文件而不是函数名。
调试符号是指带有.pdb扩展名的文件,其中包含了调试器和性能分析器用于发现执行代码信息的详细情况。Microsoft Symbol Server使我们可以通过Web连接动态地获取所需符号文件,选择Tools|Options指示Visual Studio使用该服务器,展开Debugging部分选择Symbols。
常见的性能分析问题之插装和代码覆盖
在运行插装式性能分析时,确保不要对以前其用过代码覆盖的目标进行性能分析。代码覆盖以另一种插装形式确定测试执行过程中所访问的代码,因此它们会有冲突。如果已经进行过代码覆盖,可以打开代码覆盖页面取消相关选项,重新生成解决方案就可以了。