“线程”视图(并行性能)

这是并发可视化工具中最详细、功能最丰富的视图。 通过使用此视图,可以确定线程是在执行,还是因同步、I/O 或某些其他原因阻塞执行。

在配置文件分析过程中,并发可视化工具检查每个应用程序线程的所有操作系统上下文切换事件。 上下文切换可能由多种原因导致,例如:

  • 线程被同步基元阻塞。

  • 线程的量程过期。

  • 线程发出一个阻塞 I/O 请求。

在线程已停止执行时,线程视图向每个上下文切换分配一个类别。 类别显示在视图的左下方的图例中,这些类别由其相应的帮助主题进行解释。 通过搜索已知阻塞 API 的线程调用堆栈,实现上下文切换事件的分类。 如果找不到调用堆栈匹配,则使用 Windows 提供的等待原因。尽管在技术上是正确的,但 Windows 类别可能基于实现详细信息,而不是用户的期望或意愿。例如,Windows 将本机轻量读取器/编写器锁上的阻塞报告为 I/O 而不是同步。但是,在这些情况中您应能够通过检查与上下文切换事件对应的调用堆栈,来确定任何阻塞事件的根源。

线程视图还显示线程间的依赖关系。 例如,如果您找出了同步对象上被阻塞的线程,则工具通常可以向您显示解除其阻塞的线程,以及该线程在解除相关线程的阻塞时做了些什么(通过显示其调用堆栈)。

最后,当线程正在执行时,工具将收集示例,以使您可以分析执行时间段内一个或多个线程所执行的代码。 除提供线程执行的基于示例的可见性之外,此视图还提供调用堆栈树执行分析报表和阻塞报表。

用途

“线程”视图旨在为多种用途服务。 以下为一些常见的用法:

  • 确定应用程序的用户界面 (UI) 在某些执行阶段无响应的原因。

  • 确定阻塞同步、I/O、页面错误等情况所用的时间。

  • 确定对系统中执行的其他进程的干扰程度。

  • 确定并行执行的负载平衡问题。

  • 确定伸缩性欠佳或伸缩性不存在的原因(例如,当系统中有更多逻辑内核时,并行应用程序的性能为何不提高)。

  • 了解应用程序中并发的程度,以便为并行化提供帮助。

  • 了解执行的辅助线程与关键路径之间的依赖关系。

本节的其余内容介绍了一种推荐的使用模式,以使您在此视图中体验最高的工作效率。 我们首先推荐您使用 CPU 使用率视图来集中关注相关进程执行的特定阶段。 通过利用您的应用程序中的方案标记支持,可以为此进程提供很大帮助。 放大相关的执行时间范围后,您可以选择“线程”视图。

标识并缩小感兴趣的区域

在“线程视图”中,可以看到一个时间线视图,其中 X 轴表示时间。 在 Y 轴上,您将看到,在配置文件收集期间系统中有活动的每个物理磁盘设备都对应着两个 I/O 通道,一个用于读取,另一个用于写入。 在磁盘通道的下方,进程中的每个线程都对应着一个通道。 最初,线程按照其创建顺序进行排序,这样使得主应用程序线程成为第一个线程。 可以使用视图左上角的排序选项按照其他条件(例如根据实施最多执行工作的线程)对线程排序。

接下来,您可以隐藏感兴趣的方案中未执行任何工作的线程,方法是通过从左侧的列中选择其名称,然后单击工具栏中的**“隐藏所选线程”**图标。 此类线程的存在可能有多种原因。 例如,它们可能是空闲线程池中的线程。 此类线程通常被完全阻塞(一般是因为同步)。 应从视图中将其删除,因为这些线程的统计信息只会利用不相关的信息干扰报表。

可以使用“执行分解”选项卡报表,以标识可能隐藏的其他线程。 若要查看执行分解图,请单击活动图例中的**“每线程摘要”**。 此图显示应用程序中的线程在当前可见时间范围内的线程状态明细。 为了在该图中支持伸缩性,我们限制了所显示的线程数,因此,在某些情况下该图不显示应用程序中所有线程的数据。 如果出现此情况,则在最右侧的位置中显示省略号。

现在已经将分析缩小到一个相关区域,并选择了相关的线程,下面即可开始性能分析。 以下各节将介绍可供您使用的各种工具。

线程阻塞详细信息

若要了解线程阻塞区域的基本原因,可以将鼠标指针悬停在某个此类区域上或选择(通过单击鼠标左键)某个此类区域。 将鼠标指针悬停在阻塞区域上方时,将显示一个工具提示,其中包含有关阻塞事件的常规信息,如类别、阻塞 API(如果有)、区域开始时间和阻塞持续时间。 对于抢占类别,当您的线程被内核停止时,我们还显示 CPU 中安排的进程内的进程 ID 和线程 ID。 也可以选择相应通道中的阻塞区域,这样将在底部窗口中显示当前堆栈。 除在工具提示中显示的内容外,**“当前堆栈”**选项卡将显示导致阻塞线程的调用堆栈。 通过检查调用堆栈,可以确定线程阻塞事件的基本原因。 默认情况下,我们在此视图中显示完整的调用堆栈,其中包括用户堆栈和内核堆栈。 如果工具能够确定阻塞是由特定 API 引起的,则可以在该框架之外调整调用堆栈。 如果工具无法确定导致阻塞的根函数调用,则整个调用堆栈将公开,以使用户可以对其进行检查并做出决定。

通常,执行的路径会导致多个阻塞事件。 通常,我们也有必要了解调用堆栈形成的累积阻塞延迟。 为此,我们为每个阻塞类别提供一个基于调用树的配置文件报表。 通过选择左边的某个阻塞类别图例项,可以查看配置文件。 借助这些报表,您可以很快确定应如何合理分配时间来优化应用程序。

线程间的依赖关系

并发可视化工具向您显示您的进程中阻塞线程之间的依赖关系。 若要确定哪个线程的操作解除相关线程的阻塞,请单击相关阻塞时间段。 如果工具可以确定解除阻塞的线程,则将绘制一条线,将阻塞时间段之后的执行时间段连接到其他线程。 该线显示其他线程如何解除所选线程的阻塞。 此外,还使用相关调用堆栈填充**“取消阻塞堆栈”**选项卡。 因此,您可以快速识别被阻塞线程,了解该线程尝试了哪些操作,以及查看最终允许被阻塞线程执行的因素。

线程执行详细信息

这对于确定应用程序正在执行时这些线程在执行什么代码通常很有用。 这些区域在时间线图中显示为绿色时间段。 可通过两个功能做到这一点。

首先,当您单击时间线中的执行时间段时,我们将尝试查找最近的示例配置文件调用堆栈。 一旦成功,我们就会在采用该示例的执行块中的位置上方显示一个黑色插入符号,并在**“当前堆栈”**选项卡中显示调用堆栈本身。 可以通过单击执行片段中的其他位置选择其他示例。 有时,我们无法找到示例。 这通常是由于我们收集示例配置文件的时间为 1 毫秒。 例如,如果执行时间段小于一毫秒长,则可能收集不到调用堆栈。 采样频率不能更改,但一毫秒可以很好地兼顾准确性和执行开销。

其次,带有调用树视图的执行采样配置文件报表是一项重要功能,它可以帮助您了解在何处使用了执行时间。 通过单击活动图例中的**“执行”**项可以访问此功能。 执行配置文件为当前视图中按窗口中的时间范围筛选的所有已启用(未隐藏)线程都提供一个示例报表。

时间线图

时间线图显示主机上正在进行的所有线程和所有物理磁盘设备的活动。 可以通过拖动鼠标指针、使用窗口工具栏中的缩放滑块或按住 Ctrl 的同时滚动鼠标滚轮来放大时间线。 将鼠标指针悬停在任一水平栏或时间段上,可以查看线程上该点的类别、启动时间和持续时间。 单击任一时间段,可以在**“当前堆栈”**选项卡上的屏幕下部查看调用堆栈。

在时间线图中,颜色指示在任何给定时间线程的状态。 例如,绿色时间段表示正在执行,红色时间段表示因同步被阻塞,黄色时间段表示被抢占,紫色时间段表示参与了设备 I/O。 此视图非常适于检查并行环路或并发任务中涉及的一组线程的工作平衡情况。 如果完成一个或多个线程比其他线程消耗的时间长得多,这就说明工作负载不平衡,并且有机会通过在线程间更加均匀地分配工作来提高程序的性能。

还可以使用时间线图来检查线程间的依赖关系,以及阻塞和被阻塞线程间的临时关系。 通过查看时间线上某一点的垂直切片,随时可以查看当时有多少线程正在运行。 如果当时只有一个线程是绿色的(正在执行),则意味着应用程序没有充分利用系统中可用的并发。 从工具栏上,可以单击向上和向下按钮来排序及移动各个线程,或者使用**“隐藏线程”**按钮来隐藏不相关的线程。

配置文件报表

在时间线图下面是一个包含多个报表的活动图例和选项卡式窗口。 配置文件报表会在线程视图因缩放、滚动、隐藏或取消隐藏线程而发生更改之后自动更新。 对于更大范围的跟踪,报表窗口将在计算更新的报表时变为灰色。 每个报表均包含两个筛选器调整:降噪和仅我的代码。 降噪有助于筛选耗时少的不相关调用树项。 默认值为 2%,但可以调整为 0% 到 99% 的任意值。 通过**“仅我的代码”**复选框,您可以筛选或查看您的调用树项之外的调用树项。 下一节详细描述了可用报表。

配置文件报表

通过此选项卡可以访问当前的配置文件报表。 单击活动图例中的某项可以确定显示哪个配置文件报表。 下一节中列出了可用配置文件报表,从**“执行”**开始。

当前堆栈

此选项卡显示详细信息图中所选线程片段的调用堆栈。 调用堆栈经过调整,以便侧重于与您的程序直接相关的活动。 在选定内容窗口中,“当前正在执行”线程信息将立即可见。

取消阻塞堆栈

单击**“取消阻塞堆栈”**可查看哪个线程解除了此堆栈的阻塞,以及是在代码的哪一行进行解除。

执行

执行配置文件报表显示一个细节表,其中含有每个线程在各种状态(如执行、I/O 和内存管理)中消耗的时间百分比。

单击任何相关调用树项旁边的树控件,深化了解并找到消耗执行时间的代码行。 确定相关调用树项后,右击该项即可看到包含**“查看源”“查看调用站点”的上下文菜单。 单击“查看源”定位到源代码行,而单击“查看调用站点”定位到调用此站点的代码行。 如果只有一个调用站点,则单击后将定位到该调用站点的突出显示的代码行。 如果有多个调用站点,则显示一个对话框,可能需要从中选择一项。 单击“转至源”**按钮将定位到突出显示的调用站点。 这对于选择和定位具有最多实例和/或最大时间的调用站点的源非常有用。 有关更多信息,请参见执行配置文件报表

同步

同步报表显示负责同步块的调用,以及每个调用堆栈的合计阻塞时间。 您可以使用此信息来确定和调查所关注的领域。

单击任何相关调用树项旁边的树控件,深化了解并找到消耗同步时间的代码行。 确定相关调用树项后,右击该项即可看到包含**“查看源”“查看调用站点”的上下文菜单。 单击“查看源”定位到源代码行,而单击“查看调用站点”定位到调用此站点的代码行。 如果只有一个调用站点,则单击后将连接到该调用站点的突出显示的代码行。 如果有多个调用站点,则显示一个对话框,需从中选择一项。 单击“转至源”**按钮将定位到突出显示的调用站点。 这对于选择和连接到具有最多实例和/或最大时间的调用站点的源非常有用。 有关更多信息,请参见同步时间

I/O

I/O 报表显示负责 I/O 块的调用,以及每个调用堆栈的合计阻塞时间。 您可以使用此信息来确定和调查所关注的领域。

单击任何相关调用堆栈旁边的树控件,深化了解并找到消耗 I/O 时间的代码行。 确定相关调用树项后,右击该项即可看到包含**“查看源”“查看调用站点”的上下文菜单。 单击“查看源”定位到源代码行,而单击“查看调用站点”定位到调用此站点的代码行。 如果只有一个调用站点,则单击后将连接到该调用站点的突出显示的代码行。 如果有多个调用站点,则显示一个对话框,需从中选择一项。 单击“转至源”**按钮将定位到突出显示的调用站点。 这对于选择和连接到具有最多实例和/或最大时间的调用站点的源非常有用。 有关更多信息,请参见 I/O 时间(“线程”视图)

Sleep

睡眠报表显示负责睡眠块的调用,以及每个调用堆栈的合计阻塞时间。 您可以使用此信息来确定和调查所关注的领域。

单击任何相关调用堆栈旁边的树控件,深化了解并找到消耗睡眠时间的代码行。 确定相关调用树项后,右击该项即可看到包含**“查看源”“查看调用站点”的上下文菜单。 单击“查看源”定位到源代码行,而单击“查看调用站点”定位到调用此站点的代码行。 如果只有一个调用站点可用,则单击后将连接到该调用站点的突出显示的代码行。 如果有多个调用站点可用,则显示一个对话框,需从中选择一项。 单击“转至源”**按钮将定位到突出显示的调用站点。 这对于选择和连接到具有最多实例和/或最大时间的调用站点的源非常有用。 有关更多信息,请参见睡眠时间

分页

分页报表显示发生抢占块的调用,以及每个调用堆栈的合计阻塞时间。 您可以使用此信息来确定和调查所关注的领域。 此阻塞报表的可操作性比其他报表差一些,因为抢占通常是由操作系统施加于进程的,而不是由您的代码引发的, 但显示了发生的抢占种类、发生的位置以及进程保持给定抢占状态的时间长度。

单击任何相关调用树项旁边的树控件,深入了解并找到消耗抢占时间的代码行。 确定相关调用树项后,右击该项即可看到包含**“查看源”“查看调用站点”的上下文菜单。 单击“查看源”导航到源代码行,而单击“查看调用站点”直接导航到调用此站点的代码行。 如果只有一个调用站点可用,则单击后将直接导航到该调用站点的突出显示的代码行。 如果有多个调用站点可用,则显示一个对话框,需从中选择一项。 单击“转至源”**按钮将定位到突出显示的调用站点。 这对于选择和连接到具有最多实例和/或最大时间的调用站点的源非常有用。 有关更多信息,请参见内存管理时间

抢占

分页报表显示发生抢占块的调用,以及每个调用堆栈的合计阻塞时间。 您可以使用此信息来确定和调查所关注的领域。 此阻塞报表的可操作性比其他报表差一些,因为抢占通常是由操作系统施加于进程的,而不是由您的代码引发的, 但显示了发生的抢占种类、发生的位置以及进程保持给定抢占状态的时间长度。

单击任何相关调用堆栈旁边的树控件,深入了解并找到消耗抢占时间的代码行。 确定相关调用树项后,右击该项即可看到包含**“查看源”“查看调用站点”的上下文菜单。 单击“查看源”导航到源代码行,而单击“查看调用站点”直接导航到调用此站点的代码行。 如果只有一个调用站点可用,则单击后将直接导航到该调用站点的突出显示的代码行。 如果有多个调用站点可用,则显示一个对话框,需从中选择一项。 单击“转至源”**按钮将定位到突出显示的调用站点。 这对于选择和连接到具有最多实例和/或最大时间的调用站点的源非常有用。 有关更多信息,请参见抢占时间

UI 处理

UI 处理报表显示负责 UI 处理块的调用,以及每个调用堆栈的合计阻塞时间。 您可以使用此信息来确定和调查所关注的领域。

单击任何相关调用树项旁边的树控件,深入了解并找到消耗 UI 处理时间的代码行。 确定相关调用树项后,右击该项即可看到包含**“查看源”“查看调用站点”的上下文菜单。 单击“查看源”定位到源代码行,而单击“查看调用站点”定位到调用此站点的代码行。 如果只有一个调用站点可用,则单击后将连接到该调用站点的突出显示的代码行。 如果有多个调用站点可用,则显示一个对话框,需从中选择一项。 单击“转至源”**按钮将定位到突出显示的调用站点。 这对于选择和连接到具有最多实例和/或最大时间的调用站点的源非常有用。 有关更多信息,请参见 UI 处理时间

每线程摘要

此选项卡显示一个彩色编码的列视图,其中显示每个线程在每个状态中(如运行、阻塞和 I/O)花费的总时间。 各列的底部加有标签。 在默认缩放级别中,主线程是最左边的一列。 在详细信息图中调整缩放级别时,选项卡报表将自动更新以便反映新的时间刻度。 为了在此图中支持伸缩性,我们限制了所显示的线程数。 因此,在某些情况下该图可能不显示应用程序中所有线程的数据,但将通过最右侧位置中的省略号来表示此限制。 如果您要在此图中查看的线程不存在,您可以隐藏不相关的线程直到所需线程显示在该图中。 有关更多信息,请参见“每线程摘要”报告

文件操作

此选项卡显示在磁盘 I/O 中涉及哪些线程以及这些线程接触到的文件, 其中包括加载的 DLL,读取多少字节以及其他信息。 当计算在执行过程中访问文件所用的时间时,此报表非常有用,尤其是当您的进程受到 I/O 限制。 有关更多信息,请参见文件操作报告(线程视图)

请参见

概念

并发可视化工具