您想了解如何使您的代码运行得更快,使用更少的内存,或者只是找出您的代码是否有CPU或内存问题?你当然会——你是一名开发人员!但是,内存和性能调优经常会遇到“重要但不紧急”的任务,因为真正紧急的事情,您似乎根本无法完成。所以为什么不把这个任务离你更近一点呢?
在这篇文章中,我们将看看如何在Visual Studio 2015 Update 1中使用集成了调试器的性能和内存工具。在不离开调试工作流程的情况下,这些工具可以让您快速回答诸如“我的内存占用情况如何?”“是什么在使用这些内存?”,“我应该关心这段代码的性能吗?”、“为什么这么慢?”虽然我们显示ASP。在调试Windows桌面应用程序和面向桌面的c# /VB/ c++通用Windows应用程序时,这些工具也可以工作。
如果你喜欢这篇博文,请加入我们的Visual Studio性能工具社区,了解最新情况,并帮助指导未来的功能。
发现性能和内存问题
优化代码性能的第一步是了解需要改进的地方。为了解决这个问题,PerfTips和Visual Studio 2015调试器中的诊断工具窗口为您提供内联的、可浏览的性能信息。
要使用PerfTips,只需要设置一个断点并遍历一行代码,您就会看到PerfTip出现在指令指针(黄色箭头)的右侧,并显示已经过的(时钟)时间。在本例中,它告诉我们第57行运行时间为1406ms,这通常是等待I/O(例如HTTP调用或读取fi)所花费时间的总和
如果这个数字比您希望的要高,请多运行几次相同的代码,看看是否得到一致的结果。在许多情况下,您可以单击+拖动指令指针(黄色箭头)返回以重新运行代码,而不必停止调试会话。
要查看应用程序的CPU和内存消耗,请打开诊断工具窗口(调试>显示诊断工具或Ctrl+Alt+F2):
当你开始调试时,诊断工具窗口默认打开,你可以让它打开,以便在调试时随时关注应用程序的CPU和内存消耗。
将鼠标悬停在CPU和内存图表上,你会看到一个工具提示,显示应用程序的私有内存和CPU消耗在任何时间点的百分比:
在内存图的上方,你会看到断点和步骤的时间轴,这样你就可以将CPU和内存消耗与代码的特定部分联系起来:
同样,如果您看到的值比预期的要高,请多次运行代码,看看是否得到类似的值。或者单击其中一个中断事件(矩形),将所选时间范围设置为该事件,这对于查看下一节中显示的详细CPU使用情况信息非常有用。
如果您决定进行改进,再单击几下就可以得到CPU和内存使用情况的细分,以帮助确定改进的机会。接下来我们将向您展示如何做到这一点!
降低CPU使用率
当您发现CPU使用量激增时,您的第一反应可能是尝试一些代码替代方案,看看哪一个使用最少的CPU。但是,如果您不知道问题可能是什么,或者想要验证导致问题的代码路径,请单击“诊断工具”窗口中的“CPU使用情况”选项卡,然后单击“CPU分析”开始记录CPU上运行的功能:
这样做每毫秒对CPU上的调用堆栈进行一次采样,这会给调试增加少量的性能开销。但是,它通常足够小,您可以在调试会话期间让它运行。
查看CPU数据的最简单方法是在相关代码段的开始和结束处设置断点,如下面的第55行和第65行所示。当调试器到达第55行时,按F5运行代码块(注意第65行出现的PerfTip !):
“诊断工具”窗口自动将当前时间选择设置为这两个断点之间的时间(就好像您单击了中断事件一样)。您还可以通过单击并拖动图形中的CPU峰值来设置时间范围选择。在这两种情况下,CPU使用率选项卡中的表显示了筛选到选定时间范围的调用树:
调用树提供了所有记录的调用堆栈的聚合视图。您可以展开树中的节点,以跟踪一系列函数调用(或调用堆栈的一部分),并查看下面代码路径中的CPU使用情况。总CPU (ms)显示了在给定函数及其所有父函数调用中花费的大约时间;“Total CPU(%)”提供“Total CPU (ms)”列的百分比细分。
在这里,我们看到,当GetDriverList调用DeserializeDriversJSON时,我们花费了1067ms,而GetIndividualDriverCached又调用了它,等等。右键单击任意函数a
减少内存消耗
要查看内存使用明细,请在“诊断工具”窗口中打开“内存使用情况”选项卡,然后单击“快照”。通过在内存增加之前和之后拍摄快照,您可以过滤视图,以查看在对象和堆大小方面两者之间的变化:
当您拍摄两个或多个快照时,括号内的数字显示了与表中前一个快照的差异。所有蓝色文本都是打开堆视图的超链接,以查看该快照中捕获的完整对象集,包括对象计数,它们的大小,以及该类型的所有实例的包含大小加上所有引用类型的大小:
在本例中,我们有100,025个DocumentResponse。堆中的驱动程序对象的大小总计约为91MB。
单击括号中的链接可以让您更具体地分析差异:
按包含大小差异排序将指向导致两个快照之间内存增长的对象。在本例中,diff视图显示了5个新的驱动程序对象,两个快照之间内存增长了~54MB。DriverCache、Dictionary和List对象的包含大小表明它们都是可能的罪魁祸首。
在查看被管理快照时,您可以选择一个对象,查看对该对象的引用和对该对象的引用。Paths to Root选项卡显示了其他对象对所选类型的引用:
这里我们看到DriverCache对象有一个字典,它在两个快照之间创建了5个对驱动对象的新引用。要跳转到DriverCache查看它在做什么,只需右键单击并选择Go To Definition。
引用类型选项卡显示所选类型引用的对象:
在这里,我们看到Driver对象在两个快照之间添加了50MB字节数组的引用和2MB字符串的引用。
我们在这里看到的类似步骤也适用于c++,只是有一些小区别。在调试c++时,在拍摄快照之前,首先要切换“Heap Profiling”按钮,这样您就可以查看特定类型的实例,然后查看每个实例的分配堆栈,而不是使用引用列表。