线程诊断

利用 Visual Studio 2010 中的 Concurrency Visualizer 优化性能

Hazim Shafi

如今制造商们广泛提供了多核心处理器,新处理器中的单线程性能相对而言可能就显得平淡无奇了。那就意味着,对软件开发人员来说,通过更好地利用并行机制来提高应用程序性能的压力就更大了。

并行编程是一项很有挑战性的工作,其原因很多,但我在本文中只想将重点放在并行应用程序的性能方面。多线程应用程序不止容易成为顺序实现低效率进行(如低效的算法、低速的缓存行为、过多的 I/O)的常见原因,而且还可能具有并行性能 Bug。并行性能和可伸缩性可能受到负载不平衡、同步开销过大、无意的序列化或线程迁移限制。

过去,要了解这样的性能瓶颈,需要专家级开发人员进行大量的检测和分析。即使是程序员中的佼佼者,性能优化也是一个枯燥而耗时的过程。

这种情况应该得到改善了。Visual Studio 2010 中包含了一个新的分析工具:Concurrency Visualizer,它可大大减轻并行性能分析工作的负担。此外,Concurrency Visualizer 还能帮助开发人员分析其顺序应用程序,以发现并行执行这些应用程序的可能性。在本文中,我将概括介绍 Visual Studio 2010 中 Concurrency Visualizer 的功能,并给出一些实践上的使用指导。

CPU 利用率

Concurrency Visualizer 中包含几个可视化和报告工具。有三个主要视图:分别是“CPU 利用率”、“线程”和“核心”视图。

图 1 中显示的“CPU 利用率”视图是开始使用 Concurrency Visualizer 的位置。X 轴显示从跟踪开始时起,到应用程序活动结束或跟踪结束这两个时刻中的较早时刻止,所经过的时间长度。Y 轴显示了系统中逻辑处理器核心的数量。


图 1 “CPU 利用率”视图

在我开始描述该视图的用途之前,您需要了解什么是逻辑核心,这很重要。如今的单个 CPU 芯片可能包括多个微处理器电路,这些电路即称为“物理核心”。每个物理核心可能能够同时运行多个应用程序线程。这通常称为多线程并行处理 (SMT);Intel 称它为“超线程技术”。可进行 SMT 处理的核心上每个受硬件支持的线程都向操作系统将自己呈现为一个逻辑核心。

如果在不支持 SMT 的四核心的系统上收集跟踪数据,则 Y 轴将显示 4 个逻辑核心。如果四核心系统中的每个核心都能够运行两个 SMT 线程,则 Y 轴将显示 8 个逻辑核心。这里的重点是,逻辑核心数是对可并发在系统中执行的线程数的反映,而不是对物理核心数的反映。

现在,让我们再来讨论视图。如图例中所述,图中显示了四个区域。绿色区域描述在分析运行过程中的给定时间,所分析的应用程序使用的平均逻辑核心数。其余逻辑核心处于空闲状态(以灰色显示)、由系统进程使用(以红色显示)或由系统上运行的其他进程使用(以黄色显示)。

此视图中的蓝色竖条对应于一个可选的机制,用户可用该机制来检测其代码,以将工具中的可视化内容与应用程序构造关联起来。我将在本文中稍后的部分解释如何执行这些操作。

左上方的“放大”滑块控件可用于放大视图,以查看更多详细信息,放大后,图形控件提供水平滚动条支持。也可单击鼠标左键并拖动图形本身中的区域来放大视图。

此视图有三个主要用途。首先,如果您对并行化应用程序感兴趣,则可以寻找执行区域,这些区域展示占用很多 CPU 资源的大量串行工作(在 Y 轴上的单核心级别显示为长长的绿色区域),或是 CPU 利用率不高的区域(这种区域中不显示绿色或平均利用率比 1 小得多)。这些情况都可能代表了实现并行化的机会。通过利用并行机制可加速执行频繁使用 CPU 的工作,如果区域显示 CPU 利用率出人意料地低,则可能暗示存在堵塞情况(可能由于 I/O 造成),这种情况下,可在这样的延时期间重叠执行其他有用的工作,从而实现并行运行。

第二,如果您在尝试优化并行应用程序,则可在应用程序实际运行时通过此视图确认并行程度如何。一般来说,只需查看此图,就可明显发现许多常见的并行性能 Bug。例如,您可能在该图中看到以阶梯状表示的负载不均衡情况,或者看到在本应并行执行时发生串行执行以致造成同步对象争用。

第三,由于在您的应用程序所在的系统中,还有很多其他的应用程序在执行时要争用系统资源,因此,了解您的应用程序的性能是否受其他应用程序影响非常重要。如果这种干扰是您不希望看到的,则禁用应用程序或服务以提高数据的保真度通常是个减少干扰的好办法,因为性能通常是迭代变化的。有时,干扰是由其他进程导致的,您的应用程序为提供使用体验而要与这些进程协作。无论是哪一种情况,您都能够使用此视图发现是否存在干扰,然后使用我将在后面讨论的“线程”视图来识别实际涉及到的进程。

另一项可以帮助减少干扰的功能是使用探查器命令行工具来收集跟踪数据,而不是从 Visual Studio IDE 中收集。

将您的注意力放在引起您的兴趣的某个执行窗口上,将其放大,然后切换到“线程”视图以进一步进行分析。您随时可以返回到此视图,找到您感兴趣的下一个区域,并重复此过程。

线程

图 2 中显示的“线程”视图中包含 Concurrency Visualizer 中的大部分详细分析功能和报告。您将在此视图中找到能解释在“CPU 利用率”或“核心”视图中所看到的行为的信息。您还可在此视图找到数据,以便在可能的情况下将行为与应用程序源代码关联起来。此视图有三个主要组成部分:时间线、活动图例和报告/详细信息选项卡控件。

就像“CPU 利用率”视图一样,“线程”视图在 X 轴上显示时间。(在 Concurrency Visualizer 中在视图之间进行切换时,显示在 X 轴上的时间范围保持不变。)但是,“线程”视图的 Y 轴包含两种水平通道。

顶部通道通常专用于系统上在您的应用程序分析中有活动的物理磁盘。每个磁盘有两个通道,一个用于读取,另一个用于写入。这些通道显示您的应用程序线程或系统进程线程进行的磁盘访问。(它显示系统访问,因为有时这样的访问可以代表进程反映正在进行的工作,如分页。)每一个读取或写入操作都绘制为一个矩形。矩形的长度描述访问的延迟时间,包括排队延迟,因此,多个矩形可能会重叠。

若要确定在给定时刻访问了哪些文件,请单击鼠标左键选择一个矩形。选择时,下方的报告视图将切换到“当前堆栈”选项卡,这是用于通过时间线交互显示数据的标准位置。该选项卡中将列出被读取或写入的文件的名称,具体列出何种文件名取决于所选的磁盘通道。我将稍后再来讨论 I/O 分析。

您要知道,应用程序执行的所有文件读写操作不一定都会在发生时显示出来。这是因为操作系统的文件系统使用缓冲机制,因此一些磁盘 I/O 操作无需访问物理磁盘设备就可完成。

时间线中的其余通道将列出在分析数据收集期间存在于应用程序中的所有线程。对于每个线程,如果工具检测到在探查器运行期间有任何活动,它就会在整个跟踪过程中显示该线程的状态,直至线程终止。

根据绿色“执行”类别的描述,如果某个线程正在运行,则 Concurrency Visualizer 会显示该线程利用样本分析信息在执行的操作。有两种方法可以获得此数据。一种方法是单击绿色段,这时您将在“当前堆栈”选项卡窗口中看到最近的(在一毫秒浮动范围内)分析样本调用堆栈。

还可以生成可见时间范围的样本分析报告,以了解大部分工作将时间花费在何处。如果单击活动图例中的“执行”标签,则报告将显示在“分析报告”选项卡中。分析报告有两个功能可用于减小复杂度。一个是降噪功能,默认情况下,该功能会移除生成分析样本 2% 或更少比率的调用堆栈。用户可更改此阈值。另一个功能称为“仅我的代码”,如果需要,可用于减少报告中系统 DLL 导致的堆栈帧的数量。我将在稍后更详细地讨论这些报告。

在继续说明之前,我想指出另外几个用于管理报告和视图中复杂度的功能。您将经常遇到包含很多线程的应用程序场景,其中一些线程在给定探查器运行时可能没有执行任何有用的工作。除了基于时间范围过滤报告之外,还可在 Concurrency Visualizer 中按活动线程进行过滤。如果您对真正执行工作的线程感兴趣,可使用“排序依据”选项按线程处于“执行”状态的时间百分比来将线程排序。然后可选择没有执行很多有用工作的线程组,单击右键并从上下文菜单中选择“隐藏”选项,或单击视图顶部工具栏中的“隐藏”按钮来隐藏这些线程。可以按所有线程状态类别进行排序,还可根据您的需要隐藏/显示线程。

隐藏线程的影响就是,除了在时间线上隐藏其通道之外,在所有报告中都不会再反映出它们的作用。对线程和时间范围执行过滤时,该工具中的所有统计数据和报告都会动态变化,以保持最新。

阻塞类别

线程可能由于很多原因而阻塞。“线程”视图会将每个实例归到一组阻塞类别,以尝试标识线程阻塞的原因。我之所以说“尝试”,是因为这种分类有时可能不准确(我稍后会对此进行说明),因此应该将它视作粗略的指南。即便如此,“线程”视图会显示所有的线程延迟并准确描述执行期间。您应该基于您对应用程序行为的了解,将注意力集中在视图中导致重大延迟的类别上。

此外,如果您单击阻塞事件,“线程”视图会在“当前堆栈”选项卡中提供线程停止执行时所在的调用堆栈。用户单击“当前堆栈”窗口中的堆栈帧,就可查看源代码文件(如果有)和将调用下一个函数的行号。这是该工具的一项能体现效率的重要功能。

让我们来看一下不同的阻塞类别:

同步:几乎所有的阻塞操作都可归因于 Windows 中的底层同步机制。Concurrency Visualizer 尝试将由于同步 API(如 EnterCriticalSection 和 WaitForSingleObject)而造成的堵塞事件归为此类别,但是有时在内部导致同步的其他操作可能也会归为此类别,即使这些事件可能在别处有意义也是如此。因此,这通常是要在性能优化过程中进行分析的非常重要的堵塞类别,这不仅是因为同步开销很重要,还因为该类别可反映出导致执行延迟的其他重要原因。

抢占:这包括由于线程超出在其核心上的时间份额导致的抢占。这还包括由于操作系统计划规则导致的抢占,如另一个具有较高优先级且准备好运行的进程线程的抢占。Concurrency Visualizer 还会在此处列出导致抢占的其他原因,如中断和 LPC,这些原因可能导致线程执行被中断。在每次发生这样的事件时,用户都可将鼠标悬停在抢占区域并查看工具提示,或单击黄色区域并观察“当前堆栈”选项卡的内容,从而获取接管进程 ID/名称和线程 ID。要了解“CPU 利用率”视图中黄色干扰的根本原因,这可能是很有价值的一个功能。

睡眠:此类别用于报告因线程显式请求睡眠或显式请求自动放弃对其核心的使用权而导致的线程阻塞事件。

分页/内存管理:此类别包括由于内存管理导致的阻塞事件,其中包括系统的内存管理器为响应应用程序所执行的操作而启动的所有阻塞操作。像页面错误、某些内存分配争用或某些资源的阻塞之类的事件都会显示在此处。页面错误尤其值得注意,因为这种错误可能导致 I/O。看到页面错误阻塞事件时,您不但应该检查调用堆栈,还应在磁盘通道上查找相应的 I/O 读取事件,以防页面错误需要 I/O。这种页面错误的常见原因是内核所执行的加载 DLL、内存映射 I/O 和正常的虚拟内存分页等操作。单击相应的 I/O 段获取所涉及的文件名,便可确定这是 DLL 加载还是分页。

I/O:此类别包括执行诸如文件读写、某些网络套接字操作以及注册表访问之类的操作时发生的阻塞事件。一些人认为与网络有关的一些操作可能不会在此处显示,而是在“同步”类别中显示。这是因为很多 I/O 操作都使用同步机制进行阻塞,而 Concurrency Visualizer 可能不在此类别中寻找那些 API 签名。就像“内存/分页”类别一样,当您看到看起来与访问磁盘驱动器有关的 I/O 阻塞事件时,您应该在磁盘通道中查明是否存在相应的磁盘访问。为使此操作变得更为简单,可以使用工具栏中的箭头按钮将线程移到更接近磁盘通道的位置。为此,请单击某个线程通道左侧的标签选择该通道,然后单击相应的工具栏按钮。

UI 处理:这是人们通常认为合理的唯一一种阻塞形式。它表示正在抽取消息的线程的状态。如果 UI 线程的大部分时间都处于此状态,那就表示您的应用程序是有响应的。从另一方面来说,如果 UI 线程执行过多的工作,或由于其他原因而阻塞,那么从应用程序用户的角度来看,UI 就像挂起一样。此类别提供了研究应用程序的响应能力和对其进行优化的好方法。

线程间依赖关系

“线程”视图最有价值的功能之一是它能够确定线程间的同步依赖关系。在图 2 中,我已选择了一个同步延迟段。该段已放大,且其颜色突出显示(在此例中为红色)。“当前堆栈”选项卡在此时显示线程的调用堆栈。查看该调用堆栈,您便可以确定导致线程执行被阻塞的 API。


图 2**“线程”视图**

另一个可视化功能是将阻塞段连接到另一个线程的执行段的线条。当此可视化线条可见时,它将指明最终取消了被阻塞线程的阻塞状况的线程。此外,在这种情况下,还可单击“取消阻塞堆栈”选项卡以查看取消阻塞的线程在释放被阻塞线程时在执行什么操作。

举例来说,如果阻塞线程在 Win32 关键部分中等待,则您将在其阻塞调用堆栈中看到 EnterCriticalSection 的签名。当该线程取消阻塞时,应该会在取消阻塞的线程的调用堆栈中看到 LeaveCriticalSection 的签名。分析复杂的应用程序行为时,此功能可能非常有价值。

报告

分析报告提供了一种简单的方法来确定导致应用程序的性能行为的主要因素。无论您对执行开销、阻塞开销还是磁盘 I/O 感兴趣,都可以通过这些报告着重关注可能值得研究的最重要的项目。

“线程”视图中有四种报告:“执行采样分析”、“阻塞分析”、“文件操作”和“每个线程的摘要”。所有这些报告都可使用图例来访问。例如,要查看“执行分析”报告,请单击执行图例项。此操作会在“分析报告”选项卡中生成报告。这些报告类似于图 3 中显示的内容。


图 3 典型的分析报告

对于执行分析报告,Concurrency Visualizer 会分析对应用程序执行(绿色段)进行采样时所收集的所有调用堆栈,并通过确定共享堆栈帧来对这些堆栈进行整理,从而帮助用户了解应用程序的执行结构。该工具还会计算每个帧的包含成本和排除成本。包含样本会计入给定执行路径(包括其下的所有路径)中的所有样本。排除样本则对应于调用图堆栈帧的离开样本的数量。

为获取阻塞分析,请单击图例中您感兴趣的阻塞类别。生成的报告结构类似于执行分析报告,但是包含列和排除列现在对应于归于报告中调用堆栈或帧的阻塞时间。另一列显示归于调用树中该堆栈帧的阻塞实例的数量。

在这些报告中,通过识别您的应用程序中造成大多数延时的部分,即可方便地排定性能优化工作的优先级。占用报告是信息性的,鉴于此类别的性质,这种报告通常不提供任何具备操作性的数据。您可在所有这些报告中跳转至源代码。可右键单击您感兴趣的堆栈帧来进行跳转。您可利用显示的上下文菜单跳转到函数定义(使用“查看源”选项),或跳转到应用程序中调用函数的位置(使用“查看调用站点”选项)。如果有多个调用者,则将向您显示多个选项。这样就可将诊断数据和开发过程无缝集成,以调整应用程序的行为。还可以导出这些报告以做交叉分析比较。

图 4 中显示的“文件操作”报告包括当前时间范围内的所有可见文件读和写操作的摘要。对于每个文件,Concurrency Visualizer 都会列出访问它的应用程序线程、读和写操作的数量、读或写的总字节数以及读或写的总延迟时间。除了显示直接归因于应用程序的文件操作之外,Concurrency Visualizer 还会显示系统进程执行的文件操作。如前所述,这些操作之所以显示,是因为它们可能包括系统代表您的应用程序执行的文件操作。通过导出报告可以在执行优化工作时进行交叉分析比较。


图 4 “文件操作”报告

图 5 中显示的“每线程摘要”报告为每个线程显示了一个条形图。条形图中的条分为不同的线程状态类别。要跟踪性能优化进度,这可能是很有用的工具。导出不同优化迭代的图形数据,就可记录您的进度,并可比较运行。对于线程数过多而难以全部放入该视图中的应用程序,该图不会显示其所有线程。


图 5 每线程摘要报告

核心

上下文切换过多时,可能对应用程序性能造成不利影响,在线程在恢复执行时跨核心或处理器插口进行迁移的情况下尤其如此。这是因为一个正在运行的线程要将指令及其需要的数据(通常称为工作集)加载到缓存层次结构中。当线程恢复执行时,尤其是在另一个核心上恢复执行时,它可能会在从内存或系统中的其他缓存重新加载其工作集时遇到长时间延迟。

有两种常用方法可减少此类开销。开发人员可解决底层原因来减少上下文切换的频率,也可利用处理器或核心关联。采用前一种方法通常更好,因为使用线程关联可能会造成其他性能问题,因而只应在特殊情况下使用。“核心”视图是可帮助确定线程关联带来的过多上下文切换或性能 Bug 的工具。

与其他视图一样,“核心”视图在 X 轴上显示时间线。系统中的逻辑核心显示在 Y 轴上。将对应用程序中的每个线程分配一种颜色,且线程执行段将绘制在核心通道上。如图 6 所示,将在底部窗格中显示图例和上下文切换统计数据。


图 6**“核心”视图**

这些统计数据可帮助用户识别具有过多上下文切换的线程以及导致过多核心迁移的线程。然后,用户可使用此视图将注意力集中于待查线程执行被中断的区域,或按照可见的颜色提示在核心之间来回跳转。一旦确定了描述问题的区域,用户就可将其放大并切换回“线程”视图,以了解是什么问题触发了上下文切换,如有可能则解决这些问题(例如通过减少对关键部分的争用)。有时,如果两个或更多线程争用一个核心,而其他核心空闲,则线程关联 Bug 还可自行显现。

支持 PPL、TPL 和 PLINQ

Concurrency Visualizer 不但支持现有 Windows 本机和托管编程模型,还支持 Visual Studio 2010 中提供的并行编程模型。一些新的并行构造(并行模式库 (PPL) 中的 parallel_for、任务并行库 (TPL) 中的 Parallel.For 以及 PLINQ 查询)在该性能工具中都包括可视化辅助功能,您可使用这些功能着重关注那些执行区域。

如以下示例所示,PPL 要求启用对此功能的跟踪。

Concurrency::EnableTracing();
parallel_for (0, SIZE, 1, [&] (int i2) {
  for (int j2=0; j2<SIZE; j2++) {
    A[i2+j2*SIZE] = 1.0;
    B[i2+j2*SIZE] = 1.0;
    C[i2+j2*SIZE] = 0.0;
  }
});
Concurrency::DisableTracing();

启用跟踪时,“线程”和“核心”视图将在执行开始时和结束时绘制垂直条标记,以表示 parallel_for 执行区域。垂直条通过视图顶部和底部的水平条连接。将鼠标悬停在水平条上方时,将如图 7 所示显示工具提示,显示所绘制的构造的名称。


图 7**“线程”视图中 parallel_for 可视标记的示例**

在 Concurrency Visualizer 中,TPL 和 PLINQ 不要求手动启用同等功能的跟踪。

收集分析

Concurrency Visualizer 支持用于收集分析的应用程序启动和附加方法。具体行为与 Visual Studio 探查器的用户所习惯的行为完全一样。启动图 8 中显示的“性能向导”,或使用“探查器”|“新建性能会话”选项,可通过“分析”菜单选项发起新的分析会话。在这两种情况下,都可通过选择“并发”分析方法,然后选择“可视化多线程应用程序的行为”选项激活 Concurrency Visualizer。


图 8 “性能向导分析方法”对话框

您可通过 Visual Studio 探查器的命令行工具收集 Concurrency Visualizer 跟踪数据,然后使用 IDE 分析这些跟踪数据。对无法安装 IDE 的服务器场景感兴趣的用户,可通过这个命令行工具收集跟踪数据,同时又将干扰降至最低。

您会注意到,Concurrency Visualizer 没有集成对分析 ASP.NET 应用程序的支持。但是,在运行 ASP.NET 应用程序时,可以附加到主机进程(通常是 w3wp.exe)以分析其性能。

因为 Concurrency Visualizer 使用 Windows 事件跟踪 (ETW),所以需要管理权限才能收集数据。您可以作为管理员启动 IDE,或者,系统会在需要时提醒您这样做。在后一种情况下,IDE 将用管理员权限重新启动。

将可视化项链接到应用程序阶段

Concurrency Visualizer 中的另一项功能是可选的检测库,开发人员可使用该库为他们所关心的应用程序阶段绘制标记,从而自定义视图。要在可视化项与应用程序行为之间更容易地建立关联,这个库有极高的价值。该检测库称为 Scenario 库,可从 code.msdn.microsoft.com/scenario 处的 MSDN 代码库站点下载。下面是使用 C 应用程序的一个示例:

#include "Scenario.h"
int _tmain(int argc, _TCHAR* argv[]) {
  myScenario = new Scenario(0, L"Scenario Example", (LONG) 0);
  myScenario->Begin(0, TEXT("Initialization"));

  // Initialization code goes here

  myScenario->End(0, TEXT("Initialization"));
  myScenario->Begin(0, TEXT("Work Phase"));

  // Main work phase goes here  

  myScenario->End(0, TEXT("Work Phase"));
  exit(0);
}

对该库的使用相当简单,只需包含 Scenario 头文件并链接正确的库即可。然后,创建一个或多个 Scenario 对象,并调用 Begin 和 End 方法分别标记每个阶段的开头和结尾。还要为这些方法指定每个阶段的名称。可视化项与图 7 中所示基本相同,不同之处在于工具提示将显示您在代码中指定的自定义阶段名称。此外,Scenario 标记在“CPU 使用率”视图中也可见,但其他标记并不是这样。还提供了一个同等的托管实现。

在此处应谨慎操作。应保守地使用 Scenario 标记;否则,可视化项可能会因这些标记而变得完全模糊不清。实际上,为了避免此问题,该工具在检测到过多使用这种标记的情况时,将大大减少其数量或将其完全消除。在这种情况下,可放大以显示已在大多数视图中省略的标记。此外,如果有嵌套的 Scenario 标记,只会显示最里面的标记。

资源和勘误表

Concurrency Visualizer 中有许多功能可帮助您了解其视图和报告。这样的功能中最有趣的是所有视图的右上角显示的“释义”按钮。单击“释义”,您就可以看到一个特殊的鼠标指针,您可用它来单击视图中您想获得帮助的任何功能。我们借此在该工具中提供上下文相关帮助。

此外,还有一个“提示”选项卡中含有更多帮助内容,包括指向一些常见性能问题的可视化签名库的链接。

前面提到过,该工具可利用 ETW。Concurrency Analyzer 需要的一些事件在 Windows XP 或 Windows Server 2003 中不存在,因此该工具只支持 Windows Vista、Windows Server 2008、Windows 7 和 Windows Server 2008 R2。这些操作系统的 32 位和 64 位版本都受支持。

此外,该工具还支持本机 C/C++ 和 .NET 应用程序(但不包括 .NET 1.1 和更低版本)。如果您未在受支持的平台上运行,则应该探究 Visual Studio 2010 中的另一个有价值的并发工具,该工具可通过选择“收集资源争用数据”选项来启用。

在某些情况下,在分析场景中存在大量活动时,或其他应用程序在争用 I/O 带宽时,可能会丢失重要的跟踪事件。这会导致跟踪分析时发生错误。可以通过两种方法来处理这种情况。首先,可尝试减少活动应用程序的数量再分析一次,为了尽可能减少优化应用程序时的干扰,这是一个好方法。在这种情况下,命令行工具是另一种选择。

第二,您可以增加 ETW 内存缓冲区的数量或大小。我们在输出窗口中通过一个链接提供了文档,该连接指向有关如何完成此操作的说明。如果选择第二种方法,请设置收集到合理的跟踪数据所需要的最小缓冲区总大小,因为使用这些缓冲区时,会消耗重要的内核资源。

任何诊断工具的好坏都取决于它向用户返回的数据。Concurrency Visualizer 可帮助您通过参考源代码查明性能问题的根本原因,但为了执行此操作,需要能够访问符号文件。可使用“工具”|“选项”|“调试”|“符号”对话框在 IDE 中添加符号服务器和路径。将隐式包含您当前解决方案的符号,但是在研究可以在何处找到重要的符号文件时,应该启用 Microsoft 公共符号服务器以及特定于应用程序的任何其他路径。启用符号缓存也是个好办法,这样做将大大减少分析时间,因为缓存中会填入您需要的符号文件。

虽然 ETW 提供了低开销的跟踪机制,但是 Concurrency Visualizer 收集的跟踪数据仍可能很大。分析很大的跟踪数据可能非常耗时,并且可能在实现该工具所提供的可视化功能时带来性能开销。通常,为了最大程度减少这些问题可能对您的体验造成的影响,收集分析数据的持续时间不应超过一到两分钟。对于大多数分析场景,这样长的持续时间已足以确定问题。为了避免在应用程序到达兴趣点之前收集数据,附加到正在运行的进程也是一项重要的功能。

有多个有关 Concurrency Visualizer 的信息源。请访问 Visual Studio 探查器论坛 (social.msdn.microsoft.com/forums/en-us/vstsprofiler/threads),以获取社区和开发团队对相关问题的解答。在 blogs.msdn.com/visualizeparallel 处的团队博客和 blogs.msdn.com/hshafi 处我的博客中,还提供了更多的信息。如对我们的工具有任何疑问,欢迎联系我或我的团队。我们乐于倾听 Concurrency Visualizer 的用户的意见,您的参与将帮助我们改进该工具。

 

Hazim Shafi 博士* 是 Microsoft 并行计算平台团队中的并行性能和正确性工具的架构师。他在并行、分布式计算和性能分析领域的很多方面有 15 年的经验。他具有圣塔克拉拉大学的电机工程学士学位以及莱斯大学的理工硕士学位和博士学位。*

衷心感谢以下技术专家对本文的审阅:Drake Campbell、Bill Colburn、Sasha DadiomovJames Rapp