VS2015--win32工程配置的一些想法之在 Visual Studio 2015 中进行调试的同时分析性能

Posted 江南-一苇渡江

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VS2015--win32工程配置的一些想法之在 Visual Studio 2015 中进行调试的同时分析性能相关的知识,希望对你有一定的参考价值。

出处:
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-手表) 来衡量各种点,根据需要来缩小热路径迭代添加秒表之间运行所需的多长时间。
逐句通过代码,看看如果任何特别是单步执行”感觉慢”。
所有 (”暂停”) 按钮在随机点去感受如何远的执行已取得进展的突破。在某些圈子里这指作为”穷人的采样”。
过度优化代码没有测量性能,有时通过在整个代码库应用一套性能最佳做法。
这些做法通常是不准确的不是用时间或两者都好。这就是为什么现在有性能工具在调试器中。他们将帮助您了解您的应用程序的性能,在正常调试过程中。

诊断工具窗口 您会注意到当 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。它从服务器和每张图片的缩略图中同步下载图片的列表 (一次一个)。您可以通过取消您现有的断点并将放在以下各行的新的时间这两个操作:
技术分享
图 5 改进了代码 (下图) 与 GetImagesFromCloud 方法 (顶部)

重新启动调试进程并等待,直到该应用程序命中第一个断点处。然后让应用程序能够运行 (通过按 f5 键以继续) 向第二个断点。这允许应用程序从云中检索图片的列表。然后让应用程序运行到第二个断点来衡量从云端下载缩略图。PerfTips 和打破事件告诉您花了 565 女士得到的图片列表和 6,426 的 ms 下载缩略图。性能瓶颈是在你下载的缩略图。

当你看着 CPU 使用率图 (所示图 6),该方法检索的图像列表,你可以看到它是相对较高。图形是相当平坦时表明这一过程花了很长一段时间,等待网络 I/O 的缩略图下载。
技术分享
图 6 CPU 使用率图指示延迟网络输入/输出

为了尽量减少等待客户端和服务器之间的往返时间,立即开始下载缩略图的所有操作,等待他们通过等待完成.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 方法调用。拍摄快照,通常会针对意外的内存使用情况的下一步。在内存选项卡,单击拍摄快照按钮生成堆的快照。在断点处或应用程序正在运行时,您可以拍摄快照。

如果您知道哪一行代码导致内存使用峰值,然后你有一个想法在哪里采取第一个快照。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 年新的诊断工具调试器窗口。使用这些新的集成调试工具可以帮助您提高性能,您在调试您的应用程序。



































以上是关于VS2015--win32工程配置的一些想法之在 Visual Studio 2015 中进行调试的同时分析性能的主要内容,如果未能解决你的问题,请参考以下文章

VS2015--win32工程配置的一些想法之Google Code Style中头文件的顺序

VS2015--win32工程配置的一些想法之GdiplusTypes.h(470) : error C3861: 'min': identifier not found

VS2015--win32工程配置的一些想法之算法min/max与windows中的 min/max宏冲突

0 VS2015 WIN7 配置OPENGL

龙书D3D11 Demo配置(VS2015+win10)

OpenCV3.3.0安装配置(VS2015+win7)