出处:
https://msdn.microsoft.com/zh-cn/magazine/dn973013(en-us).aspx
很多开发商花了绝大多数时间获取应用程序才干正常发挥作用。更少的时间里专注于应用程序的性能。
尽管有了非常长一段时间分析工具在 Visual Studio 中的,他们是单独的一组学习工具。很多开发者没有花时间去学习和使用它们的时候会出现性能问题。
这篇文章将介绍 Visual Studio 2015 年新的诊断工具调试器窗体。
它还将描写叙述怎样使用它来分析性能作为定期调试工作流的一部分。
我会首先提供调试器的特性和功能的概述,然后对一个深潜演练。我会告诉你怎样使用 PerfTips 来时间剖面的断点和步骤之间的代码、 怎样使用诊断工具窗体来监视 CPU 和内存,以及怎样拍摄快照来钻深进内存增长和泄漏。
这篇文章中的功能都可用于调试最托管和本机项目。
微软不断地加入很多其它的项目类型的支持、 调试配置。
有关当前受支持的功能的最新信息,请查阅诊断工具窗体在博客的帖子 aka.ms/diagtoolswindow。在这个问题上单独的一条将解释怎样在诊断工具窗体内使用 IntelliTrace (见”使用 IntelliTrace 对诊断问题更快,”) 来高速确定您的代码中的 bug 的根源。
在调试时的性能
而不是执行一个完整的分析工具。你可能会採取一个或多个下面的步骤:
将代码插入到应用程序 (比如 System.Diagnostics.Stop手表) 来衡量各种点,依据须要来缩小热路径迭代加入秒表之间执行所需的多长时间。
逐句通过代码。看看假设不论什么特别是单步执行”感觉慢”。
全部 (”暂停”) button在随机点去感受怎样远的执行已取得进展的突破。
在某些圈子里这指作为”穷人的採样”。
过度优化代码没有測量性能,有时通过在整个代码库应用一套性能最佳做法。
这些做法一般是不准确的不是用时间或两者都好。
这就是为什么如今有性能工具在调试器中。他们将帮助您了解您的应用程序的性能,在正常调试过程中。
诊断工具窗体 您会注意到当 Visual Studio 2015 年调试代码的新的诊断工具窗体中将会出现。如中所看到的的主要区别在于 图 1。这些诊断工具在两个相互补充的方式呈现信息。他们将图形加入到时间轴中窗体的上半。并提供具体的信息选项卡中的底部。
图 1 在 Visual Studio 2015 年新的诊断工具窗体
在 Visual Studio 到 2015 年,您将看到在诊断工具窗体中的三个工具:调试器 (包含 IntelliTrace)、 内存和 CPU 使用率。
您能够启用或禁用的 CPU 使用率和内存使用的工具,通过点击选择工具下拉列表。调试器工具已表明打破事件、 输出事件和 IntelliTrace 事件的三个轨道。
打破历史上重大事件和 PerfTips 打破事件让你看到每一节代码花多长时间执行。
矩形代表从应用程序開始或恢复执行,直至调试器作出它暂停时的持续时间 (见图 2)。
图 2 断裂事件和 PerfTips
矩形的起点指示你从哪里開始通过继续步进 Shift + F11 F11 F10) 或执行到光标处 (Ctrl + F10) 命令 (F5),执行应用程序。结束了该矩形指示由于它命中了断点,完毕一个步骤或由于您使用了打破全部应用程序停止的位置。
最新的中断事件的持续时间也显示在调试器中的当前行末尾处的代码。
这被称为 PerfTips。它同意您监视性能没有考虑你的眼睛离开你的代码。
在关系图下方的细节表中。也能够看到历史和打破事件和 PerfTips 表格格式的持续时间。假设你有 IntelliTrace,附加事件将显示在表中。您还能够使用筛选器显示仅仅有调试器来查看仅仅有打破事件的历史记录。
CPU 和内存分析时间轴自己主动选择时间范围内,如您设置断点并单步。
当遇到断点时,当前时间范围被重置。仅仅将最新的中断事件显示。
所选内容能够扩大到包含最新的中断事件。通过点击一个打破事件矩形或通过单击并拖动时间线上。您能够覆盖自己主动时间范围选择。
时间范围选择同意您关联范围上的 CPU 使用率和内存使用情况图。以便您能够理解代码的特定部分的 CPU 和内存的特点。
图继续更新在该应用程序执行时,让你帮我照看对 CPU 和内存作为您与您的应用程序进行交互。您能够切换到内存选项卡。拍摄快照并查看内存使用情况的具体分项数字。
IntelliTrace 性能洞察力 IntelliTrace (在 Visual Studio 社区版本号中不可用) 让你获得很多其它的洞察性能调试托管的代码时。
IntelliTrace 向调试器事件时间线中加入两个轨道:输出跟踪和 IntelliTrace 轨道。这些事件包含信息显示在输出窗体中。再加上额外的事件收集的 IntelliTrace,如异常,ADO.NET,等等。在这些轨道发生的事件也是调试器事件表所看到的。
你能够涉及的 IntelliTrace 事件穗状花序的 CPU 使用率和内存使用情况图。时间戳显示你多久各种行动在您的应用程序。比如,您能够在您的代码中加入 Debug.WriteLine 语句,使用时间戳上输出事件查看从一个语句到下一个执行所须要的多长时间。
提高性能和内存
如今。您已经看到该窗体的功能。我们将深入探讨实际用途的工具。在本节中。我们步行通过求解一组称为照片滤镜的例子应用程序中的性能问题。这个应用程序将从云端下载图片和从用户的本地图片库载入图片,这样他能够查看它们。并应用图像筛选器。
假设你想要跟着,下载源码从 aka.ms/diagtoolswndsample。由于性能是不同的不同的机器上。你会发现不同的数字。
它甚至会有所不同从执行。
慢启动应用程序当您開始调试照片滤镜应用程序时,你会发现它须要非常长的时间,要启动应用程序和载入的图片。
这是一个明显的问题。
当您在调试应用程序的功能性问题时,你往往形成一个假设并開始调试在此基础。在这样的情况下,你能猜測图片是缓慢载入。并寻找一个好的地方设置一个断点,并考验一下这个假说。
LoadImages 方法是一个伟大的地方,做到这一点。
開始和结束时的 LoadImages 函数设置断点 (如图所看到的的代码中图 3) 和開始调试 (F5)。
当代码命中第一个断点处时,按继续 (F5) 再次跑到第二个断点。
如今有两个断裂事件在时间轴中调试器事件。
图 3 LoadImages 方法
第一步显示应用程序执行仅 274 毫秒之前命中第一个断点。第二部分演示了 10,476 女士要打第二个断点之前在 LoadImages 函数中执行此代码。您还能够看到经过时间 PerfTip 在代码中显示同样的值。所以你已经缩小到 LoadImages 函数问题。
若要获取很多其它的信息和每一行须要多久的时间,又一次启动调试所以你再打第一个断点。这一次,逐句通过每一行代码中方法以查看哪些行正在最长的时间。
从 PerfTips 和调试中断事件的持续时间,你能够看到 GetImagesFromCloud 花 7,290 女士、 LoadImagesFromDisk 须要 736 女士、 LINQ 查询所需 1,322 女士和其余在小于 50 毫秒内完毕。
对于全部行时机所看到的图 4。
行号显示代表大课间活动,末尾处的线,所以线 52 意味着多长时间它接管了步线 51。 如今钻进一步入 GetImagesFromCloud 方法。
图 4 调试器事件表的每一个步骤显示经过的时间
GetImagesFromCloud 方法执行两个独立的逻辑操作,如中所看到的图 5。它从server和每张图片的缩略图中同步下载图片的列表 (一次一个)。
您能够通过取消您现有的断点并将放在下面各行的新的时间这两个操作:
图 5 改进了代码 (下图) 与 GetImagesFromCloud 方法 (顶部)
又一次启动调试进程并等待,直到该应用程序命中第一个断点处。然后让应用程序能够执行 (通过按 f5 键以继续) 向第二个断点。这同意应用程序从云中检索图片的列表。
然后让应用程序执行到第二个断点来衡量从云端下载缩略图。PerfTips 和打破事件告诉您花了 565 女士得到的图片列表和 6,426 的 ms 下载缩略图。
性能瓶颈是在你下载的缩略图。
当你看着 CPU 使用率图 (所看到的图 6),该方法检索的图像列表。你能够看到它是相对较高。图形是相当平坦时表明这一过程花了非常长一段时间,等待网络 I/O 的缩略图下载。
图 6 CPU 使用率图指示延迟网络输入/输出
为了尽量减少等待client和server之间的往返时间,马上開始下载缩略图的全部操作。等待他们通过等待完毕.NET System.Tasks 来完毕。
代替第 73 至 79 行 (从代码中图 5) 用下面的代码:
// Download thumbnails
var downloadTasks = new List<Task>();
foreach (var image in pictureList)
{
string fileName = image.Thumbnail;
string imageUrl = ServerUrl + "/Images/" + fileName;
downloadTasks.Add(DownloadImageAsync(new Uri(imageUrl), folder, fileName));
}
await Task.WhenAll(downloadTasks);
当你的时候这个新的版本号时,你能够看到须要仅仅有 2,424 女士来执行。这是关于四秒的改进。
调试内存增长和泄漏假设你看看内存使用情况图诊断慢启动时,您可能已经注意到内存使用量的急剧添加为启动该应用程序。缩略图的列表是虚拟化的列表,而仅仅有一个全然尺寸的图像显示在一段时间。
使用虚拟化的列表的长处之中的一个是它仅载入内容显示在屏幕上,所以你不会期望非常多缩略图在内存中一次。
要到这一问题的根本原因,你必须找到在代码中内存增长出现。然后。拍取快照之前和之后的增长。
比較这些快照。你会发现最有助于生长在内存中的对象类型。
内存使用情况图显示了应用程序怎样使用内存的高级视图。还有计数器命名专用字节数为您的应用程序的性能。专用字节数是内存的衡量的分配给进程总量。这还不包含与其它进程共享的内存。它包含托管的堆、 本机堆、 线程堆栈和其它内存 (如载入的.dll 文件的私人部门)。
当开发一个新的应用程序或诊断问题与现有的一个,意外的增长的内存使用情况图上会常常是你已经不表现如预期的代码的第一个迹象。
看图,您能够使用调试器功能如断点和单步执行来缩小感兴趣的代码路径。你能够从行号和持续时间显示在调试器事件选项卡又一次确定图 4负责意外增长线是线 52。LoadImagesFromDisk 方法调用。
拍摄快照,一般会针对意外的内存使用情况的下一步。
在内存选项卡,单击拍摄快照button生成堆的快照。在断点处或应用程序正在执行时。您能够拍摄快照。
假设您知道哪一行代码导致内存使用峰值。然后你有一个想法在哪里採取第一个快照。LoadImagesFromDisk 方法上设置断点和拍摄快照,当您的代码到达该断点。此快照作为基线。
接下来,单步执行的 LoadImagesFromDisk 方法,并生成还有一个快照。如今,通过比較快照,你将能够看到哪些托管的类型已加入到你跨过为函数调用的结果堆。图再一次显示了内存利用率穗正在调查 (如中所看到的图 7)。您还能够看到通过鼠标悬停图形内存 47.4 MB。
它是一个好的主意来心理记数兆字节。所以您能够稍后验证您修复了有意义的影响。
有是内存使用量明显激增
图 7 有是内存使用量明显激增
具体信息视图显示每一个快照的简要的概述。概述包含快照的顺序号,执行的时间 (以秒为单位) 时拍摄快照。堆的大小和数量的活堆上的对象。兴许快照也会显示变化的大小和对象计数从曾经的快照。
拍摄快照的过程列举了仅仅有那些仍然生活在堆上的对象。也就是说,假设对象是符合垃圾回收条件。不会包含在快照中。
这样的方式。你不须要操心当集合最后跑了。每一个快照中的数据是。仿佛刚刚发生垃圾回收。
显示快照概述在堆的大小将低于专用字节内存使用情况图表中显示。专用字节数概要显示全部类型的由您的进程分配的内存。而快照显示的大小全部活对象在托管堆上。假设你看到在内存使用情况图中。大量添加,但增长托管堆中的并不占多数的它,生长在内存中其它地方发生。
从快照概述中,您能够打开堆视图并调查按类型堆的内容。单击第二个快照,在新选项卡中打开堆视图的对象 (差异) 列中 diff 链接。单击该链接将排序堆视图中的类型由自曾经的快照创建的新对象的数目。这让你感兴趣顶部的表的类型。
堆视图快照 (见图 8) 有两个主要部分:顶部窗格和下部窗格中引用图中的对象类型表。对象类型表显示的名称、 数量和大小的每一个对象类型时拍摄快照。
中 Diff 模式的堆视图快照
图 8 中 Diff 模式的堆视图快照
几个在堆视图类型是从框架。
假设你有仅我的代码启用 (默认值)。这些都是在您的代码中引用类型或由您的代码类型引用的类型。使用此视图,您能够识别一个类型从我们靠近顶部的 table—PhotoFilter.ImageItem 的代码。
在图 8。你能够看到计数 Diff 列显示自曾经的快照创建的 137 新映像对象。顶五个新对象类型都有同样数量的新的对象,所以这些可能相关。
让我们看看第二个窗格。參考图。假设你期望要清理垃圾回收器的类型,但它仍显示在类型表中,根的路径能够帮助您跟踪下什么持有该引用。
到根的路径是參考图中这两种视图之中的一个。到根的路径是自底向上树显示完整的图形类型生根您所选的类型。假设还有一个应用程序对象保存了一个引用,植根的对象。
不必要的根的对象往往是在托管代码中内存泄露的原因。
引用的类型,还有一个视图。则相反。
为在对象类型表中,选择此视图显示其它类型的类型引用了您选定的类型。此信息能够有助于确定为什么在很多其它的内存比预期坚持所选类型的对象。这是实用的现状调查。由于类型可能会使用很多其它的内存比预期。但他们并不比它们的用处。
在对象类型表中选择 PhotoFilter.ImageItem 行。參考图将更新以显示关系图的映像。在引用类型视图中。您能够看到映像对象保留共 280 的字符串对象和 140 每一个框架的三种类型:StorageFile、 StorageItemThumbnail 和 BitmapImage。
总大小使它看起来好像贡献由映像对象保留的内存的添加最大字符串对象。专注于总大小 Diff 列使非常有意义,但数目不会导致的根本原因。
一些框架类型,如 BitmapImage,仅仅有极少量的举行在托管堆上的内存总量。
BitmapImage 实例的数量是一个更有说服力的线索。记得在照片滤镜的缩略图的列表虚拟的所以它应该载入这些映像上的需求,并使其可作为垃圾回收。当它做。然而,看起来好像提前载入全部缩略图。结合你如今了解什么 BitmapImage 对象被冰山,继续专注于那些进行调查。
PhotoFilter.ImageItem 在參考图中。右击并选择转到定义映像在编辑器中打开源文件。
映像定义的成员字段,m_photo,即 BitmapImage,如中所看到的图 9。
代码引用 m_photo 的成员字段
图 9 代码引用 m_photo 的成员字段
第一个代码路径引用 m_photo 是属性的 get 方法的照片,它是属性的数据绑定到 ListView 在 UI 中的缩略图。它看起来像 BitmapImage 正在载入 (和因此解码在本机堆上) 上的需求。
引用 m_photo 的第二个代码路径是 LoadImageFromDisk 的功能。这个项目是相应用程序的启动路径。
应用程序启动时,它获取呼吁被显示,每一个图像。
这有效地预载入 BitmapImage 的全部对象。这样的行为不利于虚拟列表视图中,作为已分配的全部内存,不管列表视图中显示图像的缩略图。该预载入算法不能非常好地扩展。很多其它的图片,你有在你的图片库中。启动内存成本也就越高。
按需载入 BitmapImage 对象是更具可扩展性的解决方式。
后停止调试器。请凝视掉线 81 和 82 在 LoadImageFromDisk 载入 BitmapImage 实例。为验证您已经固定内存性能问题而不会破坏应用程序的功能。然后又一次执行同样的实验。
按 F5,你就会看到上图总内存使用量是如今仅仅有 26.7 MB (见图 10)。
以还有一组的快照之前、 之后调用 LoadImagesFromDisk,然后比較他们。你会看到仍然是 137 的映像对象,但没有 BitmapImages (见图 11)。BitmapImages 将载入需求上,一旦你让应用程序继续启动。
内存图在固定的引用问题
图 10 内存图在固定的引用问题
參考图后固定内存的问题
图 11 參考图后固定内存的问题
正如前面提到的此调试器集成的工具还支持拍照的本机堆或托管和本机堆同一时候。堆你配置文件为基础上的调试器,您正在使用:
仅托管调试器仅仅需托管堆的快照。
仅限本机调试器 (本机项目的默认值) 仅仅须要本机堆快照。
混合模式调试器须要的托管和本机堆快照。
您能够调整此设置对您的项目属性的调试页。
当不启用调试执行工具
它是重要的是提及须要额外开销介绍了当您測量与调试器的性能。主类的开销来自您通常执行的应用程序的调试版本号的事实。
您公布到用户的应用程序将公布版本号。
在调试版本号中。编译器保持了尽可能接近原始的源码作为可能的结构和行为的可执行文件。正如您期望在调试时,一切应该工作。还有一方面。公布版本号试图优化代码的性能减少的调试体验的方式。一些例子包含衬砌中的函数调用和常数变量,移除未使用的变量和代码路径和存储变量信息可能无法读取由调试器的方式。
全部这一切意味着 CPU 密集型代码能够有时执行速度显著变慢在调试版本号中。非 CPU 密集型的操作。如磁盘 I/O 和网络调用将採取同样多的时间。通常不是记忆行为,意味着泄漏的内存会泄露和低效的内存的使用仍将显示作为大量添加这两种情况区别非常大。
它附加到目标应用程序时,将由调试器加入其它开销的类来考虑。调试器截获模块载入和异常的事件。它也提供其它所需的工作让你设置断点和步骤。
Visual Studio 会尽力筛选这样的类型的开销从性能的工具,但仍有少量的开销。
假设你看到一个应用程序的公布版本号中的一个问题。它将差点儿总是复制在调试版本号中,但不是一定在附近的其它方式。为此,调试器集成工具旨在帮助您在开发过程中主动地发现性能问题。假设您在调试版本号中发现的问题,你能够翻到公布版本号。看假设这一问题影响以及公布版本号。
可是,您可能决定往前走并在调试版本号中解决该问题,假设你决定这是预防性能良好的工作 (也就是说,修复问题减少以后遇到性能问题的机会)。假设您确定问题是非 CPU 密集型 (磁盘或网络 I/O),或假设您想要加快调试版本号,所以在开发过程中,您的应用程序是迅捷。
报告性能问题时在公布版本号中,您想要确保您能够复制和验证你已经攻克了问题。最好的办法做到这一点是翻转您为公布模式的生成与所报告的问题相符的环境中执行的工具不使用调试器。
假设你想測量业务持续时间,调试器-集成的工具将仅仅能精确到内几十毫秒量较少的系统开销。
假设你须要更高一级。执行不用调试器工具是准确性的一个更好的选择。
总结
你能够通过下载 Visual Studio 2015 RC 尝试视觉工作室 2015 年新的诊断工具调试器窗体。使用这些新的集成调试工具能够帮助您提高性能,您在调试您的应用程序。