使用 Visual Studio 对应用程序进行性能分析 - 并发

这是系列剖析文章的最后一篇,但并非最不重要的。随着多核机器的大量增长和多线程应用的发展,出现了一个重要需求——需要追踪性能以创建一个有效的多线程环境,使线程能以对机器性能影响最小的方式进行交互。Visual Studio 提供了并发剖析工具,允许开发者分析与线程交互和活动相关的数据。

本次将使用在第一篇文章中使用过的相同样本应用,以模拟一个多线程环境,在其中从不同线程多次调用相同方法。这将生成一组结果,可用于真实地查看线程应用对目标计算机性能的影响,并可视化应用性能指标本身。

开始进程

启动性能向导(通过分析菜单),选择并发方法。为使数据集尽可能具有描述性,选择了收集资源争用数据和可视化多线程应用行为的两个选项,因为样本应用涉及多个实验线程。这将为我们提供关于应用活动、CPU 和线程方面的详细指标集。
重要注意事项:使用并发方法剖析应用需要 Visual Studio 的提升权限。如果在未提升权限的情况下启动剖析会话,将出现以下消息:
此处插入图片链接
使用默认剖析设置,在向导设置期间将保持选择不变。点击下一步,直到到达最终对话框。确保在向导退出时启动会话,然后点击完成。
重要注意事项:如果在 x64 机器上运行会话,并且启用了执行分页,可能会遇到此消息。
此处插入图片链接
可以点击“是”禁用它(需要重新启动),但对于当前执行的样本会话不是必需的。
此外,如果已禁用调试符号,可能希望通过允许从Microsoft 符号服务器检索调试符号来启用它们:
此处插入图片链接
此处插入图片链接
附带说明,如果你不知道调试符号是什么,它们是代码指示器,基本上通过允许调试器读取生成错误的代码结构来允许跟踪错误。这意味着将一些变量或方法名称暴露给在测试中附加调试器到进程的开发者(或用户),以便调试代码的人可以直接看到错误的来源。

分析结果

剖析会话完成后,将呈现与其他方法完全不同的一组数据。首先,将看到有三个额外的视图可用:CPU 利用率线程核心
此处插入图片链接
在详细查看每个视图代表的内容之前,主报告页面(摘要视图)上仍有一些数据。有最争用资源,显示争用最多的资源。
争用:当一个线程尝试获取另一个线程或资源持有的锁时发生(在这种情况下,是锁争用)。进程中的争用越少越好。
此处插入图片链接
注意,这里的争用数量相当高,这不是最好的示例。为避免这种情况,可以在测试方法中引入一个锁,使最终指标看起来更像这样:
此处插入图片链接
这是一个看起来更好的图表,因为争用数量减少到最小。如果点击一个句柄,可以在视觉上看到争用按线程分离:
此处插入图片链接
水平线代表线程(通过其名称或 ID 识别),线上的每个块都是一个争用。如果点击一个线程,可以仅查看该特定单元的争用,并查看争用的调用堆栈——这可能有助于确定可能的来源以及应放置锁的位置。
最争用线程视图允许在单独线程的上下文中分析与上述相同的数据,而不是资源。
此处插入图片链接
由于通过线程池创建线程,线程名称是自动生成的。然而,如果手动为每个方法调用创建线程(通过Thread实例),则可以更改此名称。例如,以下是修改后的线程创建过程:
Program p = new Program();
string[] states = { "TX", "KS", "CA", "VA", "WA" };
foreach (string state in states)
{

Thread thread = new Thread(new ParameterizedThreadStart(p.Get));
thread.Name = state + " Thread";
thread.Start(state);

}
这是剖析视图现在显示的内容:
此处插入图片链接
由于线程已命名,更容易看到哪些线程争用最多。如果点击一个线程,将看到争用按资源分布(而不是像前一个视图中按线程分布):
此处插入图片链接
这些视图的最佳使用方式是确定需要优化的代码部分。
CPU 利用率视图还提供了一些有趣的统计信息:
此处插入图片链接
在这里可以看到应用对 CPU 的影响,还可以跟踪多个核心上的负载分布(如果存在这些核心)。注意,这里不仅绘制了应用性能,还绘制了客户端系统性能。在这种情况下,很容易将应用性能与影响 CPU 负载的实际系统活动进行比较。
直接与此统计指标相关的是核心视图。
此处插入图片链接
此视图显示多个逻辑核心(如果在分析的系统上存在)之间的线程活动分布。尽管跨核心上下文切换的数量相当高,但这是一个预期的指标,因为有大量线程。与没有额外线程的应用相比,可以看到分布略有不同:
此处插入图片链接
上下文切换通常对应用性能有负面影响——切换越多,分配给实际执行切换的时间就越多,而实际工作的时间就越少。因此,如果存在非常高数量的上下文切换,可能需要重新设计应用的线程架构。
最后但同样重要的是,有线程视图,允许查看线程性能和相关交互。
此处插入图片链接
这里的每一行都显示线程活动。各种颜色代表由阻塞调用设置的线程活动类型,在可见时间线概要框中也有概述,其中可以看到每个操作类型的百分比。对于我的应用,大多数时间线程都在同步和内存管理状态下被阻塞。请注意,这不仅适用于应用生成的线程,也适用于由 CLR 直接管理的线程。
如果查看每个线程的摘要,将看到不同的线程具有不同的操作集:
此处插入图片链接
从这个图表中可以清楚地看到,线程5148在被视为管理内存(例如,分页)的状态下被阻塞,并且在该状态下花费了相当多的功能时间,而其他线程主要在同步上被阻塞。一旦点击一个操作类型,就可以查看阻塞概要,其中揭示了为特定操作类型阻塞线程的调用。
此处插入图片链接
当前堆栈提供了有关给定线程状态的更多详细信息:
此处插入图片链接
可以清楚地看到,是WaitForMultipleObjects.aspx)API 函数将线程设置为同步状态。
在同一线程视图中,开发者能够跟踪应用管理的所有文件操作。这包括代码中定义的操作,也包括由 CLR 触发的隐含操作,如对clr.dllkernel32.dll的访问。

阅读 38
0 条评论