CPU 分析

本指南提供了可用于调查影响评估指标的中央处理单元 (CPU) 相关问题的详细技术。

特定于评估的分析指南中的各个指标或问题部分确定了需要调查的常见问题。 本指南提供了可用于调查这些问题的技术和工具。

本指南中的技术使用 Windows Performance Toolkit (WPT) 中的 Windows Performance Analyzer (WPA)。 WPT 是 Windows 评估和部署工具包 (Windows ADK) 的一部分,可以从 Windows 预览体验计划下载。 有关详细信息,请参阅 Windows Performance Toolkit 技术参考

本指南分为以下三个部分:

背景

本部分介绍如何在 Windows 10 中管理 CPU 资源。

Windows ADK 工具

本部分介绍如何在 Windows ADK 工具包中查看和解释 CPU 信息。

方法

本部分包含一系列方法,可用于调查和解决与 CPU 性能相关的常见问题。

背景

本部分包含有关 CPU 性能的简单说明和基本讨论。 有关本主题的更全面研究,我们建议阅读《Windows Internals Fifth Edition》一书。

新式计算机可以包含安装在不同套接字中的多个 CPU。 每个 CPU 可以托管多个物理处理器内核,每个内核能够同时处理一个或两个单独的指令流。 这些单独的指令流处理器由 Windows 操作系统作为逻辑处理器进行管理。

在本指南中,处理器和 CPU 均指逻辑处理器,即操作系统可用于执行程序指令的硬件设备。

Windows 10 以两种主要方式主动管理处理器硬件:电源管理,以平衡功耗和性能;和使用情况,以平衡程序和驱动程序的处理要求。

处理器电源管理

处理器并不总是处于运行状态。 当没有指令准备好执行时,Windows 会将处理器置于目标空闲状态(或 C 状态),由 Windows 电源管理器确定。 根据 CPU 使用模式,处理器的目标 C 状态将随着时间的推移进行调整。

空闲状态是从 C0(活动;非空闲)到逐渐降低功率状态的编号状态。 这些状态包括 C1(已停止,但仍启用时钟)、C2(已停止且禁用时钟)等。 空闲状态的实现是特定于处理器的。 但是,所有处理器中的状态编号越高,表明功耗越低,而且也表明处理器返回指令处理之前等待的时间越长。 在空闲状态下花费的时间会显著影响能耗和电池使用时间。

某些处理器即使在主动处理指令时也可以在性能 (P) 和限制 (T) 状态下运行。 P 状态定义处理器支持的时钟频率和电压电平。 T 状态不会直接更改时钟频率,但可以通过跳过某些时钟计时周期上的处理活动来降低有效时钟速度。 当前的 P 状态和 T 状态共同决定了处理器的有效运行频率。 较低的频率对应于较低的性能和较低的功耗。

Windows 电源管理器根据 CPU 使用模式和系统电源策略为每个处理器确定适当的 P 状态和 T 状态。 在高性能状态与低性能状态下花费的时间会显著影响能耗和电池使用时间。

处理器使用情况管理

Windows 使用三个主要抽象来管理处理器的使用情况。

  • 进程

  • 线程

  • 延迟过程调用 (DPC) 和中断服务例程 (ISR)

进程和线程

Windows 中的所有用户模式程序都在进程的上下文中运行。 一个进程包括以下属性和组件:

  • 一个虚拟地址空间

  • 优先级类

  • 加载的程序模块

  • 环境和配置信息

  • 至少一个线程

尽管进程包含程序模块、上下文和环境,但是它们并不直接按计划在处理器上运行。 而是进程拥有的线程按计划在处理器上运行。

线程维护执行上下文信息。 几乎所有计算都作为线程的一部分进行管理。 线程活动从根本上影响度量和系统性能。

由于系统中处理器的数量有限,因此不能同时运行所有线程。 Windows 实现了处理器分时,这允许一个线程在处理器切换到另一个线程之前运行一段时间。 在线程之间切换的行为称为上下文切换,它由称为调度程序的 Windows 组件执行。 调度程序根据优先级、理想处理器和相关性、量子和状态做出线程调度决策。

优先级

优先级是调度程序如何选择运行哪个线程的一个关键因素。 线程优先级是一个从 0 到 31 的整数。 如果线程是可执行的,并且其优先级高于当前正在运行的线程,则较低优先级线程会立即被抢占,较高优先级线程会进行上下文切换。

当线程正在运行或已准备好运行时,除非有足够的处理器可以同时运行这两个线程,否则不能运行较低优先级线程,或者除非较高优先级线程限制为仅在可用处理器的子集上运行。 线程具有可在某些时间暂时提升为更高优先级的基本优先级:例如,当进程拥有前台窗口时,或者当 I/O 完成时。

理想的处理器和相关性

线程的理想处理器和相关性决定了计划运行给定线程的处理器。 每个线程都有一个由程序设置或由 Windows 自动设置的理想处理器。 Windows 使用循环机制方法,以便将每个进程中大致相等数量的线程分配给每个处理器。 如果可能,Windows 会调度一个线程在其理想的处理器上运行;但是,该线程有时可以在其他处理器上运行。

线程的处理器相关性限制运行线程的处理器。 这是比线程的理想处理器属性更强的限制。 程序使用 SetThreadAffinityMask 设置相关性。 相关性可以防止线程在特定的处理器上运行。

Quantum

上下文切换是成本高昂的操作。 Windows 通常允许每个线程在切换到另一个线程之前运行一段时间(这称为量子)。 量子持续时间旨在保留明显的系统响应能力。 它通过最大程度地减少上下文切换开销来最大程度地提高吞吐量。 量子持续时间可能因客户端和服务器而异。 在服务器上,量子持续时间通常更长,以牺牲明显的响应为代价最大程度地提高吞吐量。 在客户端计算机上,Windows 总体上分配较短的量子,但为与当前的前台窗口关联的线程提供更长的量子。

状态

每个线程在任何给定时间都处于特定的执行状态。 Windows 使用与性能相关的三种状态;这些状态是:正在运行、就绪和正在等待。

当前正在执行的线程处于“正在运行”状态。 可以执行但当前未在运行的线程处于“就绪”状态。 由于正在等待特定事件而无法运行的线程处于“正在等待”状态。

状态到状态转换如“图 1 线程状态转换”所示:

图 1 线程状态转换

图 1 线程状态转换

图 1 线程状态转换说明如下:

  1. 处于“正在运行”状态的线程通过调用等待函数(例如 WaitForSingleObject 或 Sleep(> 0))来启动到“正在等待”状态的转换。

  2. 正在运行的线程或内核操作准备好处于“正在等待”状态的线程(例如,SetEvent 或计时器到期)。 如果处理器处于空闲状态,或者准备好的线程的优先级高于当前正在运行的线程,则准备好的线程可以直接切换到“正在运行”状态。 否则,它将进入“就绪”状态。

  3. 当正在运行的线程等待、生成 (Sleep(0)) 或达到其量子的末尾时,调度程序会调度一个处于“就绪”状态的线程进行处理。

  4. 当处于“正在运行”状态的线程被更高优先级的线程抢占、生成 (Sleep(0)) 或当其量子结束时,调度程序会将其切换为“就绪”状态。

处于“正在等待”状态的线程不一定表示存在性能问题。 大多数线程花费大量时间处于“正在等待”状态,这允许处理器进入空闲状态并节省能源。 仅当用户等待线程完成操作时,线程状态才成为性能的一个重要因素。

DPC 和 ISR

除了处理线程,处理器还响应来自硬件设备(如网卡或计时器)的通知。 当硬件设备需要处理器注意时,它会生成中断。 Windows 通过挂起当前正在运行的线程并执行与中断关联的 ISR 来响应硬件中断。

在执行 ISR 期间,可以阻止处理器处理任何其他活动,包括其他中断。 因此,ISR 必须快速完成,否则系统性能可能会降低。 为了缩短执行时间,ISR 通常计划 DPC 以执行响应中断时必须完成的工作。 对于每个逻辑处理器,Windows 维护计划的 DPC 队列。 DPC 优先于任何优先级级别的线程。 在处理器返回到处理线程之前,它会执行其队列中的所有 DPC。

在处理器执行 DPC 和 ISR 期间,该处理器上不能运行任何线程。 对于必须以特定吞吐量或精确计时执行工作的线程(例如播放音频或视频的线程),此属性可能会导致问题。 如果用于执行 DPC 和 ISR 的处理器时间阻止这些线程接收足够的处理时间,则线程可能无法达到其所需的吞吐量或无法按时间完成其工作项。

Windows ADK 工具

Windows ADK 将硬件信息和评估写入评估结果文件。 WPA 在各种图表中提供有关 CPU 使用情况的详细信息。 本部分介绍如何使用 Windows ADK 和 WPA 收集、查看和分析 CPU 性能数据。

Windows ADK 评估结果文件

由于 Windows 仅支持对称多处理系统,因此本部分中的所有信息都适用于所有已安装的 CPU 和内核。

详细的 CPU 硬件信息可在评估结果文件 EcoSysInfo 部分的 <Processor><Instance id=”0”> 节点下找到。

例如:

<Processor>
  <Instance id="0">
    <ProcessorName>The name of the first CPU</ProcessorName>
    <TSCFrequency>The maximum frequency of the first CPU</TSCFrequency>
    <NumProcs>The total number of processors</NumProcs>
    <NumCores>The total number of cores</NumCores>
    <NumCPUs>The total number of logical processors</NumCPUs>
    ...and so on...

WPA 图

将跟踪加载到 WPA 后,可以在 WPA UI 的“跟踪/系统配置/常规”和“跟踪/系统配置/PnP”部分下找到处理器硬件信息。

注意 本指南中的所有过程都发生在 WPA 中。

CPU 空闲状态图

如果在跟踪中收集空闲状态信息,则“电源/CPU 空闲状态”图将显示在 WPA UI 中。 此图始终包含有关每个处理器的“目标”空闲状态的数据。 如果处理器支持此状态,该图还将包含有关每个处理器的实际空闲状态的信息。

下表中的每一行描述了处理器“目标”或“实际”状态的空闲状态更改。 以下各列可用于图中的每一行:

详细信息

CPU

受状态更改影响的处理器。

进入时间

处理器进入空闲状态的时间。

退出时间

处理器退出空闲状态的时间。

Max:Duration(ms)

处于空闲状态的时间(默认为 aggregation:maximum)。

Min:Duration(ms)

处于空闲状态的时间(默认为 aggregation:minimum)。

下一个状态

处理器在当前状态之后转换到的状态。

上一个状态

处理器在转换到当前状态之前所处的状态。

状态

当前空闲状态。

状态(数值)

当前空闲状态作为数字(例如,0 表示 C0)。

Sum:Duration(ms)

处于空闲状态的时间(默认为 aggregation:sum)。

未使用

类型

“目标”(针对电源管理器为处理器选择的目标状态)或“实际”(针对处理器的实际空闲状态)。

默认 WPA 配置文件为此图提供了两个预设:按类型、CPU 显示的状态和按类型、CPU 显示的状态图。

按类型、CPU 显示的状态

每个 CPU 的“目标”和“实际”状态与“按类型、CPU 显示的状态”图中 Y 轴上的状态编号一起绘制。 图 2 CPU 空闲状态(按类型、CPU 显示的状态)显示了 CPU 在活动和目标空闲状态之间波动的“实际”状态。

图 2 CPU 空闲状态(按类型、CPU 显示的状态)

图 2 CPU 空闲状态(按类型、CPU 显示的状态)

按类型、CPU 显示的状态图

在此图中,每个 CPU 的“目标”和“实际”状态以时间线格式显示。 每个状态在时间线中都有一个单独的行。 图 3 CPU 空闲状态(按类型、CPU 显示的状态图)在时间线视图中显示了与图 2 CPU 空闲状态(按类型、CPU 显示的状态)相同的数据。

图 3 CPU 空闲状态(按类型、CPU 显示的状态图)

图 3 CPU 空闲状态(按类型、CPU 显示的状态图)

CPU 频率图

如果 CPU 频率数据是在支持多个 P 状态或 T 状态的系统上收集的,则“CPU 频率”图将在 WPA UI 中可用。 下表中的每一行表示处理器特定频率级别的时间。 “频率 (MHz)”列包含与处理器支持的 P 状态和 T 状态相对应的有限数量的频率。 以下各列可用于图中的每一行:

详细信息

持续时间百分比

持续时间表示为当前可见时间段内总 CPU 时间的百分比。

计数

频率更改的数量(对于单个行始终为 1)。

CPU

受频率更改影响的 CPU。

进入时间

CPU 进入 P 状态的时间。

退出时间

CPU 退出 P 状态的时间。

频率 (MHz)

CPU 处于 P 状态期间的频率。

Max:Duration(ms)

处于 P 状态的时间(默认为 aggregation:maximum)。

Min:Duration(ms)

处于 P 状态的时间(默认为 aggregation:minimum)。

Sum:Duration(ms)

处于 P 状态的时间(默认为 aggregation:sum)。

未使用

类型

有关 P 状态的其他信息。

默认配置文件为此图定义了“按 CPU 显示的频率”预设。 图 4 按 CPU 显示的 CPU 频率显示了在三个 P 状态之间转换的 CPU:

图 4 按 CPU 显示的 CPU 频率

图 4 按 CPU 显示的 CPU 频率

CPU 使用率(已采样)图

“CPU 使用率(已采样)”图中显示的数据表示按常规采样间隔采集的 CPU 活动样本。 在大多数跟踪中,此间隔为一毫秒(1 ms)。 表中的每一行都表示单个样本。

该样本的权重表示该样本相对于其他样本的显著性。 权重等于当前样本的时间戳减去上一个样本的时间戳。 由于系统状态和活动的波动,权重并非始终完全等于采样间隔。

图 5 CPU 采样表示收集数据的方式:

图 5 CPU 采样

图 5 CPU 采样

此采样方法不会记录样本之间发生的任何 CPU 活动。 因此,持续时间很短的活动(例如 DPC 和 ISR)在“CPU 采样”图中没有很好地表示。

以下各列可用于图中的每一行:

详细信息

权重百分比

权重表示为当前可见时间范围内花费的总 CPU 时间的百分比。

地址

位于堆栈顶部的函数的内存地址。

全部计数

行所表示的样本数。 此数目包括处理器空闲时采集的样本。 对于单个行,此列始终为 1。

计数

行表示的样本数,不包括处理器空闲时采集的样本。 对于单个行,此列始终为 1(或 0,对于 CPU 处于低功耗状态的情况)。

CPU

采集此样本的 CPU 的从 0 开始的索引。

显示名称

活动进程的显示名称。

DPC/ISR

样本测量的是常规 CPU 使用率、DPC/ISR 还是低功耗状态。

函数

位于堆栈顶部的函数。

模块

包含堆栈顶部的函数的模块。

优先级

正在运行的线程的优先级。

进程

拥有正在运行的代码的进程的映像名称。

进程名

拥有正在运行的代码的进程的全名(包括进程 ID)。

堆栈

正在运行的线程的堆栈。

线程 ID

正在运行的线程的 ID。

线程启动函数

正在运行的线程所启动的函数。

线程启动模块

包含线程启动函数的模块。

时间戳

采集样本的时间。

重量

样本表示的时间(以毫秒为单位)(即自上一个样本以来的时间)。

默认配置文件为此图提供以下预设:

  • 按 CPU 显示的利用率

  • 按优先级显示的利用率

  • 按进程显示的利用率

  • 按进程和线程显示的利用率

按 CPU 显示的利用率

“CPU 使用率(按 CPU 显示的利用率)”图显示了如何在处理器之间分布工作。 图 6 CPU 使用率(按 CPU 显示的利用率)显示了两个 CPU 的这种分布:

图 6 CPU 使用率(按 CPU 显示的利用率)

图 6 CPU 使用率(按 CPU 显示的利用率)

按优先级显示的利用率

按线程优先级分组的 CPU 使用率显示了高优先级线程如何影响较低优先级线程。 图 7 CPU 使用率(已采样)(按优先级显示的利用率)显示了此图:

图 7 CPU 使用率(已采样)(按优先级显示的利用率)

图 7 CPU 使用率(已采样)(按优先级显示的利用率)

按进程显示的利用率

按进程分组的 CPU 使用率显示了进程的相对使用情况。 图 8 CPU 使用率(已采样)(按进程显示的利用率)显示了此预设。 此样本图显示一个进程比其他进程消耗更多的 CPU 时间。

图 8 CPU 使用率(已采样)(按进程显示的利用率)

图 8 CPU 使用率(已采样)(按进程显示的利用率)

按进程和线程显示的利用率

按进程分组然后按线程分组的 CPU 使用率显示了进程的相对使用情况以及每个进程中的线程。 图 9 CPU 使用率(已采样)(按进程和线程显示的利用率)显示了此预设。 在此图中选择单个进程的线程。

图 9 CPU 使用率(已采样)(按进程和线程显示的利用率)

图 9 CPU 使用率(已采样)(按进程和线程显示的利用率)

CPU 使用率(精确)图

“CPU 使用率(精确)”图记录与上下文切换事件关联的信息。 每行表示一组与单个上下文切换相关联的数据;也就是说,当线程开始运行时。 针对以下事件序列收集数据:

  1. 新线程换出。

  2. 新线程已准备好由就绪线程运行。

  3. 新线程换入,从而无法换出旧线程。

  4. 新线程再次换出。

在图 10 CPU 使用率(精确)图中,时间从左侧流向右侧。 图标签对应于“CPU 使用率(精确)”图中的列名。 “时间戳”列的标签显示在图的顶部,“间隔持续时间”列的标签显示在图的底部。

图 10 CPU 使用率(精确)图

图 10 CPU 使用率(精确)图

图 10 CPU 使用率(精确)图中时间线的中断将时间线划分为可以在不同 CPU 上同时发生的区域。 只要未修改带编号事件的顺序,这些时间线就会重叠。 例如,就绪线程可以在处理器 2 上运行,同时一个新线程换出,然后又回到处理器 1 上。

在时间线上记录了以下四个目标的信息:

  • 新线程,即换入的线程。 它是图中此行的主要焦点。

  • NewPrev 线程,指的是上一次新线程换入的时间。

  • 就绪线程,即准备好要处理的新线程的线程。

  • 旧线程,即在换入新线程时换出的线程。

下表中的数据与每个目标线程相关:

详细信息

CPU 使用率

切换新线程后的 CPU 使用率。 此值表示为当前可见时间段内总 CPU 时间的百分比。

计数

行表示的上下文切换数。 对于单个行,此值始终为 1。

Count:Waits

行所表示的等待数。 对于单个行,此值始终为 1,但线程切换到空闲状态的情况除外;在这种情况下,它设置为 0。

CPU

发生上下文切换的 CPU。

CPU 使用率(毫秒)

上下文切换后新线程的 CPU 使用率。 这等于 NewInSwitchTime,但以毫秒为单位显示。

IdealCpu

调度程序为新线程选择的理想 CPU。

LastSwitchOutTime (s)

上一次新线程换出的时间。

NewInPri

换入的新线程的优先级。

NewInSwitchTime(s)

NextSwitchOutTime(s) 减去 SwitchInTime(s)

NewOutPri

新线程换出时的优先级。

NewPrevOutPri

新线程先前换出时的优先级。

NewPrevState

新线程先前换出后的状态。

NewPrevWaitMode

新线程先前换出时的等待模式。

NewPrevWaitReason

新线程换出的原因。

NewPriDecr

影响线程的优先级提升。

NewProcess

新线程的进程。

NewProcess 名称

新线程的进程的名称,包括 PID。

NewQnt

未使用。

NewState

新线程换入后的状态。

NewThreadId

新线程的线程 ID。

NewThreadStack

新线程换入时的堆栈。

NewThreadStartFunction

新线程的启动函数。

NewThreadStartModule

新线程的启动模块。

NewWaitMode

新线程的等待模式。

NewWaitReason

新线程换出的原因。

NextSwitchOutTime(s)

下一次换出新线程的时间。

OldInSwitchTime(s)

旧线程在换出之前换入的时间。

OldOutPri

旧线程换出时的优先级。

OldProcess

拥有旧线程的进程。

OldProcess 名称

拥有旧线程的进程的名称,包括 PID。

OldQnt

未使用。

OldState

旧线程换出后的状态。

OldThreadId

旧线程的线程 ID。

OldThreadStartFunction

旧线程的启动函数。

OldThreadStartModule

旧线程的启动模块。

OldWaitMode

旧线程的等待模式。

OldWaitReason

就线程换出的原因。

PrevCState

处理器的上一个 CState。 如果此值不是 0(活动),则处理器在新线程上下文换入之前处于空闲状态。

Ready(s)

SwitchInTime(s) 减去 ReadyTime (s)

就绪 ThreadId

就绪线程的线程 ID。

就绪 ThreadStartFunction

就绪线程的启动函数。

就绪 ThreadStartModule

就绪线程的启动模块。

ReadyingProcess

拥有就绪线程的进程。

ReadyingProcess 名称

拥有就绪线程的进程的名称,包括 PID。

ReadyThreadStack

就绪线程的堆栈。

ReadyTime (s)

新线程就绪的时间。

SwitchInTime(s)

新线程换入的时间。

TimeSinceLast (s)

SwitchInTime(s) 减去 LastSwitchOutTime (s)

Waits (s)

ReadyTime (s) 减去 LastSwitchOutTime (s)

默认配置文件为此图使用以下预设:

  • 按 CPU 显示的时间线

  • 按进程、线程显示的时间线

  • 在上下文切换开始时按优先级显示的使用率

  • 按 CPU 显示的利用率

  • 按进程、线程显示的利用率

按 CPU 显示的时间线

每个 CPU 时间线上的 CPU 使用率显示了工作在处理器之间的分布情况。 图 11 CPU 使用率(精确)(按 CPU 显示的时间线)显示了八处理器系统上的时间线:

图 11 CPU 使用率(精确)(按 CPU 显示的时间线)

图 11 CPU 使用率(精确)(按 CPU 显示的时间线)

按进程、线程显示的时间线

每个进程、每个线程时间线上的 CPU 使用率显示哪些进程在特定时间运行了线程。 图 12 CPU 使用率(精确)(按进程、线程显示的时间线)显示了跨多个进程的时间线:

图 12 CPU 使用率(精确)(按进程、线程显示的时间线)

图 12 CPU 使用率(精确)(按进程、线程显示的时间线)

在上下文切换开始时按优先级显示的使用率

此图标识每个优先级级别的高优先级线程活动突发。 图 13 CPU 使用率(精确)(在上下文切换开始时按优先级显示的使用率)显示了优先级的分布:

图 13 CPU 使用率(精确)(在上下文切换开始时按优先级显示的使用率)

图 13 CPU 使用率(精确)(在上下文切换开始时按优先级显示的使用率)

按 CPU 显示的利用率

在此图中,CPU 使用率按 CPU 进行分组和绘制,以显示工作在处理器之间的分布情况。 图 14 CPU 使用率(精确)(按 CPU 显示的使用率)显示了具有八个处理器的系统的此图。

图 14 CPU 使用率(精确)(按 CPU 显示的使用率)

图 14 CPU 使用率(精确)(按 CPU 显示的使用率)

按进程、线程显示的利用率

在此图中,CPU 使用率先按进程分组,然后按线程分组。 它显示了进程和每个进程中线程的相对使用情况。图 15 CPU 使用率(精确)(按进程、线程显示的利用率)显示了这种跨多个进程的分布:

图 15 CPU 使用率(精确)(按进程、线程显示的利用率)

图 15 CPU 使用率(精确)(按进程、线程显示的利用率)

DPC/ISR 图

DPC/ISR 图是 WPA 中 DPC/ISR 信息的主要源。 图中的每一行都表示一个片段,该片段是 DPC 或 ISR 不间断运行的时间段。 在片段的开始和结尾收集数据。 DPC/ISR 完成后,将收集其他数据。 图 16 DPC/ISR 关系图显示了此操作的工作原理:

图 16 DPC/ISR 关系图

图 16 DPC/ISR 关系图

图 16 DPC/ISR 关系图描述了在下列活动期间收集的数据:

  1. DPC/ISR-A 开始运行。

  2. 中断级别高于 DPC/ISR-A 的设备中断会导致 ISR-B 中断 DPC/ISR A,从而结束 DPC/ISR-A 的第一个片段。

  3. ISR-B 完成并因此结束 ISR-B 的片段。 DPC/ISR-A 恢复第二个片段中的执行。

  4. DPC/ISR-A 完成,从而结束 DPC/ISR-A 的第二个片段。

每个片段对应于数据表中显示的一行。 DPC/ISR-A 的片段与非片段列共享相同的信息。

DPC/ISR 图的列描述片段级别的信息或 DPC/ISR 级别的列。 每个片段在片段级列中的数据不同,在 DPC/ISR 列中的数据相同。

详细信息

持续时间百分比(已分段)

持续时间(已分段),表示为当前可见时间段内总 CPU 时间的百分比。

独占持续时间百分比

独占持续时间,表示为当前可见时间段内总 CPU 时间的百分比。

非独占持续时间百分比

非独占持续时间,表示为当前可见时间段内总 CPU 时间的百分比。

地址

DPC 或 ISR 函数的内存地址。

计数 (DPC/ISR)

此行表示的 DPC/ISR 的计数。 对于表示 DPC/ISR 的最终片段的行,此计数始终为 1;否则,此计数为 0。

计数(分段)

行表示的分段数。 对于单个行,此值始终为 1。

CPU

运行 DPC 或 ISR 的逻辑处理器的索引。

DPC 类型

对于 DPC,DPC 的类型为“常规”或“计时器”。 对于 ISR,此值为空。

DPC/ISR 进入时间(秒)

DPC/ISR 启动时跟踪中的时间。

DPC/ISR 退出时间(秒)

从跟踪开始到 DPC/ISR 完成的时间。

持续时间(已分段)(毫秒)

片段退出时间(秒)减去片段进入时间(秒),以毫秒为单位。

独占持续时间(毫秒)

此 DPC/ISR 的所有片段的分段持续时间总和(以毫秒表示)。

Fragment

如果此行的 DPC/ISR 有多个片段,则此值为 True;否则为 False。

Fragment

如果这不是此 DPC/ISR 的唯一片段,则此值为 True;否则为 False。

片段进入时间(秒)

片段开始运行的时间。

片段退出时间(秒)

片段停止运行的时间。

函数

已运行 DPC 或 ISR 函数。

非独占持续时间(毫秒)

DPC/ISR 退出时间(秒)减去 DPC/ISR 进入时间(秒),以毫秒为单位。

MessageIndex

消息信号中断的中断索引。

模块

包含 DPC 或 ISR 函数的模块。

返回值

DPC/ISR 的返回值

类型

事件的类型;此类型是 DPC 或中断 (ISR)。

向量

设备上中断向量的值。

默认配置文件为此图使用以下预设:

  • [DPC,ISR,DPC/ISR] 按 CPU 显示的持续时间

  • [DPC,ISR,DPC/ISR] 按模块、函数显示的持续时间

  • [DPC,ISR,DPC/ISR] 按模块、函数显示的时间线

[DPC,ISR,DPC/ISR] 按 CPU 显示的持续时间

DPC/ISR 事件按运行它们的 CPU 聚合,并按持续时间排序。 此图显示了 DPC 活动在 CPU 之间的分配。 图 17 按 CPU 显示的 DPC/ISR 持续时间显示了具有八个处理器的系统的此图。

图 17 DPC/ISR(按 CPU 显示的持续时间)

图 17 按 CPU 显示的 DPC/ISR 持续时间

[DPC,ISR,DPC/ISR] 按模块、函数显示的持续时间

DPC/ISR 事件在此图中按 DPC/ISR 例程的模块和函数聚合,并按持续时间排序。 这显示了哪些 DPC/ISR 例程消耗的时间最多。图 18 DPC/ISR(按模块、函数显示的持续时间)显示了在两个模块中引发 DPC/ISR 活动的时间段:

图 18 DPC/ISR(按模块、函数显示的持续时间)

图 18 DPC/ISR(按模块、函数显示的持续时间)

[DPC,ISR,DPC/ISR] 按模块、函数显示的时间线

DPC/ISR 事件在此图中按 DPC/ISR 例程的模块和函数聚合。 它们绘制为时间线。 此图提供了运行 DPC/ISR 的时间段的详细视图。 此图还可以显示单个 DPC/ISR 的分段情况。 图 19 DPC/ISR(按模块、函数显示的时间线)显示了三个模块中的活动时间线:

图 19 DPC/ISR(按模块、函数显示的时间线)

图 19 DPC/ISR(按模块、函数显示的时间线)

堆栈树

堆栈树显示在 WPA 中的 CPU 使用率(已采样)、CPU 使用率(精确)和 DPC/ISR 表中,以及评估报告中报告的问题中。 堆栈树可显示一段时间内与多个事件关联的调用堆栈。 树中的每个节点都表示由事件子集共享的堆栈段。 树由各个堆栈构成,如“图 20 来自三个事件的堆栈”所示:

图 20 来自三个事件的堆栈

图 20 来自三个事件的堆栈

图 21 标识的常见段显示了如何为此图标识常见序列:

图 21 标识的常见段

图 21 标识的常见段

图 22 由堆栈构建的树显示了如何组合常见段以形成树的节点:

图 22 由堆栈构建的树

图 22 由堆栈构建的树

WPA UI 中的“堆栈”列包含每个非叶节点的扩展器。 在评估报告的问题中,树与聚合权重一起显示。 如果某些分支的权重不满足指定的阈值,则可以从关系图中删除这些分支。 下面的示例堆栈演示如何将上面表示的事件显示为评估报告问题的一部分。

5ms   ModuleA!Function1
5ms   ModuleA!Function2
5ms   ModuleA!Function3
      |
4ms   |-ModuleA!Function4
4ms   |   ModuleB!Function1
| &nbsp; |
1ms   |   |-ModuleB-Function2
1ms   |   |    ModuleB-Function3
| &nbsp; |
3ms   |   |-ModuleB!Function3
3ms   |        ModuleB!Function4
      |
1ms   |-ModuleA!Function5
1ms        ModuleC!Function1
1ms        ModuleC!Function2

堆栈中的 <itself> 节点表示函数本身位于堆栈顶部的时间。 <itself> 节点不包括父函数调用的函数所花费的时间。 该持续时间称为函数中花费的独占时间。

例如,Function1 调用 Function2。 Function2 在 CPU 密集型循环中花费了 2 毫秒,并调用了另一个运行了 4 毫秒的函数。 这可以通过以下堆栈表示:

6ms   ModuleA!Function1
      |
2ms   |-<itself>
4ms   |-ModuleA!Function2
4ms        ModuleB!Function3
4ms        ModuleB-Function4

方法

本部分介绍性能分析的标准方法。 它提供了可用于调查常见 CPU 相关性能问题的方法。

性能分析是一个四步过程:

  1. 定义方案和问题。

  2. 确定所涉及的组件和相关时间范围。

  3. 创建应该发生的情况的模型。

  4. 使用模型来识别问题并调查根本原因。

定义方案和问题

性能分析的第一步是明确定义方案和问题。 许多性能问题会影响通过评估指标度量的方案。 例如:

方案 1:物理资源未得到充分利用。 例如,服务器无法充分利用网络连接,因为它无法足够快地加密数据包。

方案 2:物理资源被过度利用。 例如,系统在使用电池电源的空闲期间使用大量 CPU 资源。

方案 3:活动未按所需速率完成。 例如,在视频播放期间会丢弃帧,因为帧解码速度不够快。

方案 4:活动延迟。 例如,用户启动了 Internet Explorer,但打开标签页的时间比预期的要长。

本指南介绍了与 CPU 资源相关的方案 3 和 4。 方案 1 和 2 超出了范围,未进行介绍。 若要分析这些问题,可以从不明确的观察开始,例如“速度过慢”,并询问其他问题来确定方案和确切问题。

确定组件和时间段

确定方案和问题后,可以确定所涉及的组件和相关的时间段。 组件包括硬件资源、进程和线程。

通常可以通过在分析指南中识别关联的活动来查找相关的时间范围。 活动是在 WPA 中可以选择和放大的开始事件和停止事件之间的间隔。 如果未定义活动,则可以通过查找与方案关联的特定泛型事件,或者查找可能标记方案开始和结束的资源利用率变化来查找时间范围。 例如,如果 CPU 空闲 2 秒,然后充分利用 4 秒,然后再次空闲 2 秒,则 4 秒的充分利用可能是捕获视频播放的跟踪中相关的区域。

创建模型

若要了解问题的根本原因,必须具有应该发生的情况的模型。 模型从问题或指标的任何关联目标开始;例如,“此操作应在 5 秒内完成。”

更完整的模型包含有关组件应执行方式的信息。 例如,组件之间应进行什么通信? 典型的资源利用率是什么? 操作通常需要多久?

通常可以在评估分析指南中找到模型的信息。 如果该资源不可用,则可以从没有表现出性能问题的类似硬件和软件生成跟踪,以创建模型。

使用模型确定问题,然后调查根本原因

有了模型后,可以将跟踪与模型进行比较,以确定问题所在。 例如,名为 “挂起设备 ”的特定活动的模型可能建议整个活动应在三秒内完成,而名为 “挂起 <设备名称> ”的子活动的每个实例花费的时间不应超过 100 毫秒。 如果子活动 “挂起 <设备名称> ”的两个实例各花费 800 毫秒,则应调查这些实例。

可以分析与模型的每个偏差以找到根本原因。 应检查所涉及的线程的状态,并查找常见的根本原因。 针对未按所需速率完成的活动或延迟的活动,下面描述了几个与 CPU 相关的主要原因:

直接 CPU 使用率:适当的线程收到了充分的 CPU 资源,但所需的程序执行速度不够快。 这可能是由程序故障或硬件缓慢引起的。

线程干扰:某个线程未获得足够的运行时间,因为其他线程正在运行。 在这种情况下,该线程被认为是发生饥饿或被抢占。

DPC/ISR 干扰:线程未获得足够的运行时间,因为 CPU 忙于处理 DPC 或 ISR。

在许多情况下,其中一个根本原因不会明显影响线程,并且线程大部分时间都处于等待状态。 在这种情况下,必须确定并调查线程正在等待的事件。 这种递归类型的调查称为“等待分析”,它首先确定关键路径。

高级方法:等待分析和关键路径

活动是指从开始事件流向结束事件的一种操作网络,一些操作顺序执行和一些操作并行执行。 跟踪中的任何开始/结束事件对都可以视为一个活动。 通过此操作网络的最长路径称为“关键路径”。 减少关键路径上任何操作的持续时间会直接缩短整个活动的持续时间,尽管它也可以更改关键路径。

图 23 活动操作显示了三个线程的活动。 线程 1 发送活动开始事件,然后等待线程 2 和线程 3 完成它们的任务。 线程 2 首先完成它的任务,然后线程 3 完成它的任务。 当两个线程都完成了各自的任务后,将线程 1 准备就绪并完成活动事件。

图 23 活动操作

图 23 活动操作

在此方案中,关键路径包含线程 3 和线程 1 的部分。 这些是在图 24 关键路径中进行跟踪的。 由于线程 2 不在关键路径上,因此完成其任务所用的时间不会影响总体活动时间。

图 24 关键路径

图 24 关键路径

关键路径是“为什么某个活动花费了这么多时间才能完成”这一问题的低级别字面回答。 了解关键路径的关键段之后,可以对其进行分析,以找出导致总体延迟的问题。

查找关键路径的一般方法

查找关键路径的第一步是查看方案模型,以了解活动的用途和实现情况。

了解活动有助于识别可能位于关键路径上的特定操作、进程和线程。 例如,“快速启动恢复资源管理器初始化”活动的延迟可能是由 RunOnce 应用程序和资源管理器初始化过程引起的,这两者都需要大量的 I/O。

查看方案模型后,检查评估是否报告了受影响活动的任何问题。 很多时候,在评估报告的延迟问题中,会有大致的关键路径。 关键路径显示为等待和就绪操作的序列。 它可以作为一系列事件自始至终读取,并将关键路径的主要延迟段置于列表中间。 列表中的最后一项是准备完成活动的线程的操作。

如果必须手动查找关键路径,建议你确定完成活动的进程和线程,并从活动完成的时刻逆向工作。 通过 WPA 中的“活动”图,可以标识启动活动的进程和线程,以及完成活动的进程和线程。

“活动”图显示通过评估结果 XML 文件加载跟踪的时间。 若要确定与特定活动关联的进程和线程,请将关系图展开到相关活动,然后将视图切换到“关系图+表”。 将关系图模式设置为“表”。 为表中的每个活动显示“启动进程”、“启动线程 ID”、“结束进程”和“结束线程 ID”列。

了解启动和结束进程、线程和活动的实现后,可以逆向跟踪关键路径。 首先分析完成该活动的线程,以确定该线程如何花费其大部分时间:正在运行、就绪或正在等待。

大量运行时间表明直接 CPU 使用率可能影响关键路径的持续时间。 在就绪模式下花费的时间表明其他线程通过阻止关键路径上的线程执行来影响关键路径的持续时间。 等待花费的时间指向 I/O、计时器或当前线程正在等待的关键路径上的其他线程和进程。

准备好当前线程的每个线程都可能是关键路径中的另一个环节,并且还可以进行分析,直到考虑到关键路径的持续时间。

过程:在 WPA 中查找关键路径

下面的过程假定你已在“活动”图中确定了要为其查找关键路径的活动。

  1. 可以通过将鼠标悬停在“活动”图中的活动上来确定完成活动的进程。

  2. 添加“CPU 使用率(精确)”图。 缩放到受影响的活动,并应用“按进程、线程显示的利用率”预设。

  3. 右键单击列标题,使“ReadyThreadStack”和“CPU Usage (ms)”列可见。 删除“Ready (us) [Max]”和“Waits (us) [Max]”列。

  4. 展开目标进程,并将其分别按“CPU Usage (ms)”、“Ready (us) [Sum]”和“Waits (us) [Sum]”进行排序。

  5. 在“正在运行”、“就绪”或“正在等待”状态下花费的时间最长的进程中搜索 NewThreadId。

    在“正在运行”或“就绪”状态下花费大量时间的线程可能表示关键路径上的直接 CPU 使用率。 请注意它们的线程 ID。在“正在等待”状态下花费大量时间的线程可能正在等待 I/O、计时器或关键路径中的另一个线程。

  6. 若要了解线程正在等待的内容,请展开“NewThreadId”组以显示“ReadyThreadStack”。

  7. 展开“[Root]”。

  8. 以 KiDispatchInterrupt 开头的堆栈不与其他线程相关。 若要确定线程在这些堆栈中等待的内容,请展开“KiDispatchInterrupt”并查看子堆栈上的函数。 IopfCompleteRequest 指示就绪线程正在等待 I/O。 KiTimerExpiration 指示就绪线程正在等待计时器。

  9. 展开不以 KiDispatchInterrupt 开头的堆栈,直到看到“ReadyingProcess”和“ReadyingThread”。 如果该进程已展开,则展开对应于“ReadyingThread”的“NewThreadId”。 重复此步骤,直到找到正在运行、就绪、正在等待其他原因或正在等待其他进程的线程为止。 如果该线程正在等待其他进程,请使用该进程重复此过程。

示例

此示例显示“快速启动恢复资源管理器初始化”活动的延迟。 “问题”窗格中的搜索显示针对此活动报告了七个延迟类型的问题。 其中每个问题都可以作为关键路径的一部分进行审查。 确定了以下关键段:

  • 进程 TestBootStrapper.exe (3024) 的线程 3872 被抢占 2.1 秒。

  • 进程 TestBootStrapper.exe (3024) 的线程 3872 使用 1 秒的 CPU 时间。

  • 进程 TestBootStrapper.exe (3024) 的线程 3872 将注册表配置单元刷新 544 毫秒。

  • 进程 TestBootStrapper.exe (3024) 的线程 3872 休眠 513 毫秒。

  • Explorer.exe 的线程 4052 和 4036 从磁盘读取,导致 461 毫秒延迟。

  • 进程 TestBootStrapper.exe (3024) 的线程 3872 发生饥饿 187 毫秒。

  • 进程 TestBootStrapper.exe 的线程 3872 将 3.5MB 写入磁盘,导致 178 毫秒延迟。

问题显示此活动延迟了 5.2 秒。 这些延迟会使大部分活动的总体持续时间达到 6.3 秒。 TestBootStrapper.exe 应用程序主要负责延迟,主要是因为它抢占了其他处理任务。

调查关键路径中的问题

  1. 缩放到受影响的区域并添加“ReadyThreadStack”和“CPU Usage (ms)”列。

  2. 在这种情况下,Explorer.exe 是完成活动的进程。 展开 explorer.exe 进程,并将其分别按“CPU Usage (ms)”、“Ready (us) [Sum]”和“Waits (us) [Sum]”进行排序,如以下图所示:

    图 25 按 CPU Usage (ms) 显示的活动

    图 25 按 CPU Usage (ms) 显示的活动

    图 26 按 Ready (us) 显示的活动

    图 26 按 Ready (us) 显示的活动

    图 27 按 Waits (us) 显示的活动

    图 27 按 Waits (us) 显示的活动

  3. 按“CPU Usage (ms)”列排序显示顶部子行 299 毫秒。 按“Ready (us) [Sum]”列排序显示顶部子行 46 毫秒。 按“Waits (us) [Sum]”列排序显示顶部子行 5749 毫秒,显示第二行 4902 毫秒。 由于这些行对延迟有显著影响,因此应进一步调查它们。

  4. 展开堆栈以显示就绪线程,如以下图所示:

    就绪进程和就绪线程

    图 28 一个线程的就绪进程和就绪线程

    图 29 另一个线程的就绪进程和就绪线程

    图 29 另一个线程的就绪进程和就绪线程

    在此示例中,第一个线程大部分时间都在等待 RunOnce.exe 进程退出。 应调查为什么 RunOnce.exe 进程需要这么多时间才能完成。 第二个线程正在等待第一个线程,并且可能是同一等待链中的一个无关紧要的环节。

  5. 对 RunOnce.exe 重复此过程中的步骤。 主要参与列是“Waits (us)”,并且它有四个可能的参与者。

  6. 展开每个参与者,可以看到前三个参与者各自正在等待第四个参与者。 这种情况使得前三个参与者对等待链没有影响。 第四个参与者正在等待另一个进程 TestBootStrapper.exe。

    此方案如“图 30 RunOnce.exe 中一个线程的就绪进程和就绪线程”所示:

    图 30 RunOnce.exe 中一个线程的就绪进程和就绪线程

    图 30 RunOnce.exe 中一个线程的就绪进程和就绪线程

  7. 对 TestBootStrapper.exe 重复此过程中的步骤。 结果如以下三个图所示:

    图 31 按 CPU Usage (ms) 显示的线程

    图 31 按 CPU Usage (ms) 显示的线程

    图 32 按 Ready (us) 显示的线程

    图 32 按 Ready (us) 显示的线程

    图 33 按 Waits (us) 显示的线程

    图 33 按 Waits (us) 显示的线程

    线程 3872 用大约 1 秒运行,用 2 秒准备,用 1.3 秒等待。 由于此线程也是线程 3872 的就绪线程,因此运行和就绪时间可能会造成延迟。 评估报告以下时间与延迟匹配的问题:

    • 进程 TestBootStrapper.exe (3024) 的线程 3872 被抢占 2.1 秒。

    • 进程 TestBootStrapper.exe (3024) 的线程 3872 发生饥饿 187 毫秒。

    • 进程 TestBootStrapper.exe (3024) 的线程 3872 使用 1 秒的 CPU 时间。

  8. 若要查找其他导致问题,请查看线程 3872 正在等待的事件。 展开“ReadyThreadStack”以查看等待 1.3 秒的参与者,如“图 34 等待时间的参与者”所示:

    图 34 等待时间的参与者

    图 34 等待时间的参与者

    KiRetireDpcList 通常与 I/O 相关,KiTimerExpiration 是计时器。 可以通过删除“ReadyThreadStack”,然后查看“NewThreadStack”来查看 I/O 和计时器的启动过程。 此视图显示三个相关函数,如“图 35 NewThreadStack 上的 I/O 和计时器”所示:

    图 35 NewThreadStack 上的 I/O 和计时器

    图 35 NewThreadStack 上的 I/O 和计时器

    此视图公开了以下详细信息:

    • 进程 TestBootStrapper.exe (3024) 的线程 3872 将注册表配置单元刷新 544 毫秒。

    • 进程 TestBootStrapper.exe (3024) 的线程 3872 休眠 513 毫秒。

    • 进程 TestBootStrapper.exe 的线程 3872 将 3.5MB 写入磁盘,从而导致 178 毫秒延迟。

  9. 当你开始调查关键路径时,你已分析了 Explorer.exe 中最重要的等待原因,并忽略了该等待原因之后发生的关键路径的任何部分。 若要捕获关键路径中以前忽略的此部分,必须查看时间线。 添加“CPU 使用率(精确)”并应用“按进程、线程显示的时间线”预设。

  10. 筛选以仅包含标识为关键路径一部分的进程。 生成的图如“图 36 关键路径时间线”所示:

    图 36 关键路径时间线

    图 36 关键路径时间线

    图 36 关键路径时间线显示 Explorer.exe 在停止等待 RunOnce.exe 后执行了更多工作。 缩放到之前分析的等待链后的时间段,并执行另一个分析。 在这种情况下,分析显示大量线程是 Explorer.exe 内部的线程,并且在关键路径上没有明确的踪迹。 在这种情况下,进一步分析不太可能产生可操作见解。

直接 CPU 使用率

由于关键路径上的线程占用了大量 CPU 时间,因此活动通常会延迟。 通过使用线程状态模型,可以看到此问题的特征是关键路径上的线程在“正在运行”状态下花费了特别多的时间。 在某些硬件上,这种高 CPU 使用率会导致延迟。

问题识别

许多评估使用启发式方法来识别与直接 CPU 使用率相关的问题。 关键路径上的大量 CPU 使用率报告为以下形式的问题:

进程 P 的 CPU 使用将受影响的活动 A 延迟 x 秒

其中 P 是正在运行的进程,A 是活动,x 是时间(以秒为单位)。

如果针对导致延迟的活动报告了这些问题,则直接 CPU 使用率可能是导致问题的原因。

调查直接 CPU 使用率

  1. 可以通过在“CPU 使用率(已采样)”图中查找导致 100% CPU 使用率的各个 CPU 来手动识别问题。

  2. 缩放到图中相关的区域并选择“按进程和线程显示的利用率”预设。

    默认情况下,该表在顶部显示聚合 CPU 使用率最高的行。 这些线程还显示在“CPU 使用率(已采样)”图的顶部。

    注意 在具有多个处理器的系统上,使用 100% 单个处理器的线程似乎会消耗 100/(逻辑处理器数)。 在这种系统上,只有虚拟空闲线程(PID 0,TID 0)才能显示出比 100/(逻辑处理器数)更高的处理器利用率。 如果消耗最多 CPU 的进程和线程对应于关键路径中的任意线程,则直接 CPU 使用率可能是一个因素。

直接评估报告的直接 CPU 使用率问题的示例

TestUM.exe 进程 (4024) 使用的 CPU 将受影响的活动(快速启动关闭进程 TestIM.exe)延迟 2.1 秒。 此示例如“图 37 线程 3208”所示:

图 37 线程 3208

图 37 线程 3208

调查

发现直接 CPU 使用率会导致关键路径出现延迟后,必须确定导致延迟的特定模块和函数。

方法:查看评估报告的直接 CPU 使用率问题

可以展开评估报告的直接 CPU 使用率问题,以显示受直接 CPU 使用率影响的关键路径。 如果展开与 CPU 使用率关联的节点,将显示与 CPU 使用率和关联的模块相关联的堆栈。 此视图如“图 38 展开的 CPU 使用率段”所示:

图 38 展开的 CPU 使用率段

图 38 展开的 CPU 使用率段

方法:手动浏览直接 CPU 使用率的堆栈问题

如果评估未报告问题,或者如果你需要额外验证,可以使用“CPU 使用率(已采样)”图手动收集有关 CPU 使用率问题所涉及的模块和函数的信息。 为此,必须缩放到相关的区域,并查看按 CPU 使用率排序的堆栈。

手动浏览直接 CPU 使用率的堆栈问题

  1. 在“跟踪”菜单上,单击“加载符号”。

  2. 缩放时间线,仅显示受 CPU 问题影响的关键路径部分。

  3. 应用“按进程和线程显示的利用率”预设。

  4. 将“Stack”列添加到显示内容中,然后将此列拖动到“Thread ID”的右侧(栏左侧)。

  5. 展开进程和线程以显示堆栈树。

    堆栈中的行按“CPU 使用率的权重百分比”以降序排序。 这会将最相关的堆栈置于顶部。 展开堆栈时,请注意“权重百分比”列,以确保将注意力集中在使用率最高的行上。

  6. 若要提取堆栈的副本,请选择所有行,右键单击,然后单击“复制所选内容”。

解决方法

可以在配置和组件级别应用补救措施来解决高 CPU 使用率问题。

直接 CPU 使用率对具有低端处理器的计算机产生的影响更大。 在这些情况下,可以向计算机添加更多的处理能力。 或者,你或许能够从关键路径或系统中删除有问题的模块。 如果可以更改组件,请考虑重新设计工作以实现以下结果之一:

  • 从关键路径中删除占用大量 CPU 的代码

  • 使用 CPU 效率更高的算法

  • 延迟或缓存工作

线程干扰

不在关键路径上(并且可能与活动无关)的线程的 CPU 使用率可能会导致关键路径上的线程被延迟。 线程状态模型表明,此问题的特征是关键路径上的线程在“就绪”状态下花费了异常多的时间。

问题识别

许多评估使用启发式方法来识别与干扰相关的问题。 这些报告采用以下两种形式之一:

  • 进程 P 发生饥饿。 饥饿导致受影响的活动 A 延迟 x 毫秒。

  • 进程 P 被抢占。 抢占导致受影响的活动 A 延迟 x 毫秒。

其中 P 是进程,A 是活动,x 是时间(以毫秒为单位)。

第一种形式反映来自与关键路径上的线程具有相同优先级的线程的干扰。 第二种形式反映来自优先级高于关键路径上的线程的线程的干扰。

如果针对延迟的活动报告了这些类型的问题,则线程干扰可能是导致问题的原因。 可以使用“CPU 使用率(精确)”图来手动确定问题。

确定线程干扰问题

  1. 缩放到间隔并应用“按 CPU 显示的利用率”预设。 所有 CPU 的利用率为 100% 表示存在干扰问题。

  2. 应用“按进程、线程显示的利用率”预设,并按第一个“Ready (us)”列排序。 (这是包含“求和”聚合的列。)

  3. 展开受影响的活动的进程,并查看关键路径上的线程就绪时间。 此值是通过解决任何线程干扰问题可以减少延迟的最长时间。 相对于被调查的延迟具有显著幅度的值表明存在线程干扰问题。

“图 39 CPU 利用率接近 100%”和“图 40 线程干扰问题”表示此方案:

图 39 CPU 利用率接近 100%

图 39 CPU 利用率接近 100%

图 40 线程干扰问题

图 40 线程干扰问题

调查

确定问题后,必须确定受影响的线程在“就绪”状态下花费了很多时间的原因。

方法:确定线程在“就绪”状态下花费时间的原因

可以使用“CPU 使用率(精确)”图来确定线程在“就绪”状态下花费时间的原因。 必须首先确定线程是否仅限于某些处理器。 虽然无法直接获取此信息,但可以在 CPU 利用率较高的时间段内检查线程的 CPU 使用率历史记录。 这是线程倾向于频繁在处理器之间切换的时间段。

确定线程的处理器限制

  1. 缩放到受影响的区域。

  2. 添加“CPU 使用率(精确)”图,并应用“按进程、线程显示的利用率”预设。

  3. 使用”高级”对话框在 NewThreadId 右侧添加一个具有“非重复计数”聚合模式的 Cpu 列。

  4. 筛选图表以仅显示你感兴趣的线程。

    Cpu 列中的值反映了当前时间间隔内运行该线程的处理器数量。 在 CPU 利用率为 100% 期间,此数字接近允许运行此线程的处理器数量。 如果值小于可用处理器的数量,则线程可能仅限于某些 CPU。

    图 41 受限制的线程提供此图的示例:

    图 41 受限制的线程

    图 41 受限制的线程

了解线程的处理器限制后,可以确定是什么抢占了线程或使线程发生饥饿。 为此,必须确定线程在“就绪”状态下花费的时间间隔,然后检查在这些时间间隔内运行的其他线程或进程。

确定是什么抢占了线程或使线程发生饥饿

  1. 构造一个图表,显示线程何时处于“就绪”状态并应用“按进程、线程显示的利用率”预设。

  2. 打开“查看编辑器”,单击“高级”,然后选择“图表配置”选项卡。

  3. 将“开始时间”设置为“ReadyTime (s)”,并将“持续时间”设置为“Ready (us)”,如“图 42 就绪时间列”所示。 单击 “确定”

    图 42 就绪时间列

    图 42 就绪时间列

  4. 在“视图编辑器”中,将“CPU Usage (%)”列替换为“Ready (us) [Sum]”列。

  5. 选择相关的线程,生成类似于“图 43 就绪时间图”的图:

    图 43 就绪时间图

    图 43 就绪时间图

  6. 在这种情况下,线程在“就绪”状态下花费了大量时间。 若要确定其典型优先级,请将“平均”聚合添加到“NewInPri”列。

    在这种情况下,线程的平均优先级正好是 8。 此数字表明它可能是永远不会收到优先级提升的后台线程。

  7. 知道平均优先级后,请查看允许运行线程的 CPU 的 CPU 活动。

    在这种情况下,线程被确定为仅具有 CPU 1 的相关性。

  8. 添加另一个“CPU 使用率(精确)”图,并应用“按 CPU 显示的利用率”预设。 选择相关 CPU。

  9. 打开“高级”视图并添加之前找到的优先级筛选器,以筛选出该线程。 此方案如“图 44 线程筛选器”所示:

    图 44 线程筛选器

    图 44 线程筛选器

    在“图 45 CPU 使用率、就绪时间和其他线程活动”中,最上面的图显示线程 3548 的 CPU 使用率。 中间的图显示线程准备就绪的时间,底部的图显示允许运行线程的 CPU(在本例中为 Cpu1)上的活动。

    图 45 CPU 使用率、就绪时间和其他线程活动

    图 45 CPU 使用率、就绪时间和其他线程活动

  10. 放大线程在该时间间隔内的大部分时间已准备就绪但未运行的区域。

  11. 在“CPU 使用率”图中,将“NewInPri”添加到栏的左侧并检查结果。

    优先级等于目标线程优先级的线程或进程显示线程发生饥饿的时间。 优先级高于目标线程优先级的线程或进程显示线程被抢占的时间。 可以通过添加所有抢先式线程和操作的时间来计算线程被抢占的总时间。

    图 46 目标线程就绪时按优先级显示的使用率显示 730 毫秒的线程时间被抢占,300 毫秒的线程时间发生饥饿。 (此图放大到 1192 毫秒间隔。)

    图 46 目标线程就绪时按优先级显示的使用率

    图 46 目标线程就绪时按优先级显示的使用率

  12. 若要确定哪些线程负责此线程的抢占和饥饿,请将“NewProcess”列添加到“NewInPri”列的右侧,并查看进程运行的优先级别。 在这种情况下,抢占和饥饿主要是由同一进程中的另一个线程和 TestResidentApp.exe 引起的。 可以假设这些进程会收到高于其基本优先级的定期优先级提升。

解决方法

可以通过更改配置或组件来解决抢占或饥饿问题。 请考虑下列补救措施:

  • 从系统中删除有问题的进程。

  • 调整有问题的进程的基本优先级…

  • 更改有问题的进程运行的时间;例如,在计算机重启时延迟其启动时间。

  • 如果可以更改有问题的组件,请重新设计这些组件,使其减少 CPU 占用量或以较低优先级运行。

DPC/ISR 干扰

如果运行 DPC 和 ISR 消耗了过多的处理器时间,则可能没有足够的可用 CPU 时间来运行线程。 这种情况可能会导致线程干扰的类似延迟。 如果线程必须以常规的高频速率完成操作(例如在视频播放或动画中),DPC 和 ISR 的干扰可能会导致操作问题。

问题识别

许多评估使用启发式方法来识别与 DPC/ISR 相关的问题。 如果将 DPC/ISR 活动报告为以下形式的问题,则将其标识为可疑:

在 P 期间,DPC D 超过了阈值 m 毫秒 x 次。此 DPC 的 n 个实例总共运行 t 毫秒。

其中 D 是 DPC,m 是设置阈值的毫秒数,x 是 DPC 超过阈值的次数,P 是当前进程,n 是 DPC 运行的实例数,t 是 DPC 运行超过阈值的总时间(以毫秒为单位)。

例如,评估会报告以下问题:

DPC sdbus.sys!SdbusWorkerDpc 在媒体引擎生命周期内超过 3.0 毫秒的目标 153 次。 此 DPC 的 153 个实例总共运行 864 毫秒

如果针对出现问题事件或延迟的活动报告了此问题,则 DPC/ISR 活动可能是导致问题的原因。

手动标识 DPC/ISR 干扰

  1. 若要手动标识 DPC/ISR 干扰,请在 WPA 中打开跟踪,并识别相关的问题事件。 这些是特定于评估的一般事件,例如,Microsoft-Windows-Dwm-Core:SCHEDULE_GLITCH 或 Microsoft-Windows-MediaEngine:DroppedFrame。

  2. 在事件关系图旁边,添加“按 CPU 显示的 DPC/ISR 持续时间”图。 如果“按 CPU 显示的 DPC/ISR 持续时间”图中的峰值与问题事件对齐,则 DPC/ISR 可能是导致问题的因素。

  3. 如需更多数据,请放大显示几个问题事件前 100 毫秒的时间段。 如果问题事件发生前 100 毫秒区域内的一个或多个处理器上显示重大 DPC/ISR 活动,则可以断定问题事件是由 DPC/IRS 活动引起的。

  4. 若要确定 DPC/ISR 干扰是否导致延迟,请缩放到显示正在运行的线程的区域。 记下运行此线程的一个或多个 CPU。

  5. 在 DPC/ISR 图中,应用“按 CPU 显示的 DPC/ISR 持续时间”预设,并查看该时间范围内相关 CPU 上的 DPC/ISR 活动。

图 47 问题事件和 DPC/ISR 活动显示 iexplore.exe 的线程 864 与受影响的活动相关。 线程 864 在 CPU2 上处于“正在运行”状态的时间占可见时间范围的 10.65%。 但是,DPC/ISR 图显示 CPU2 忙于执行 DPC/ISR 的时间占该时间的 10%。

注意 大多数 DPC/ISR 的影响没有本示例中显示的那么大。

图 47 问题事件和 DPC/ISR 活动

图 47 问题事件和 DPC/ISR 活动

在“图 48 与问题事件无关的 DPC/ISR”中,显示了与性能问题无关的 DPC/ISR:

图 48 与问题事件无关的 DPC/ISR

图 48 与问题事件无关的 DPC/ISR

在“图 49 DPC/ISR 干扰导致的延迟”中,显示了会导致性能问题的 DPC/ISR:

图 49 DPC/ISR 干扰导致的延迟

图 49 DPC/ISR 干扰导致的延迟

调查

在确定 DPC/ISR 与问题或延迟相关后,必须确定涉及哪些特定 DPC/ISR,以及它们为何频繁发生或运行时间过长。

方法:查看评估报告的 DPC/ISR 问题

在评估报告的 DPC/ISR 问题中,可以展开显示由 DPC 或 ISR 抢占的主要进程的问题。 展开堆栈以查看与受影响的活动最相关的进程的 DPC 活动,如图中所示,展开堆栈以了解 DPC 的作用。 图 50 展开的 DPC 堆栈显示了展开的堆栈:

图 50 展开的 DPC 堆栈

图 50 展开的 DPC 堆栈

方法:查找最长持续时间的 DPC/ISR 并查看堆栈

如果评估未报告 DPC/ISR 存在问题,则可以使用“DPC/ISR”和“CPU 使用率(已采样)”图获取最相关 DPC 的堆栈信息。 建议找到相关的 DPC/ISR,记下它的模块和函数,然后在“CPU 使用率(已采样)”图中找到示例以获取完整的堆栈信息。

查找最长持续时间的 DPC/ISR 并查看堆栈

  1. 缩放到相关的间隔。

  2. 在 DPC/ISR 图中,选择预设“按模块、函数显示的 DPC/ISR 持续时间”。

    如果已加载符号,则 DPC/ISR 事件将按总持续时间进行排序,然后按模块和函数细分。 列表中的最前面几行包含可能导致事件问题的 DPC/ISR 事件。 记下模块和函数名称。

  3. 在“CPU 使用率(已采样)”图中,选择“按进程显示的利用率”预设。 默认情况下,此预设隐藏 DPC/ISR 活动。

  4. 打开“视图编辑器”,然后单击“高级”。

  5. 在“筛选器”选项卡上,将“隐藏与筛选器匹配的行”设置更改为“保留与筛选器匹配的行”。 这会使 DPC/ISR 活动显示出来。

  6. 删除“Process”列并添加“Stack”列以查看按堆栈排序的 DPC/ISR。

  7. 清除当前行选定内容。

  8. 右键单击“Stack”列中的单元格,然后单击“在此列中查找”。

  9. 输入在此过程的步骤 2 中记下的模块和函数。

  10. 选中“添加到当前选定内容”,然后单击“全部查找”以选择该函数的所有实例。

  11. 选择所有行后,右键单击并单击“蝶形/查看被调用方”。

此视图显示按总持续时间排序的此特定函数的活动。 此视图类似于在评估报告的问题的详细视图中显示的堆栈。 “Weight”列接近堆栈上的每个函数所花费的非独占时间(以毫秒为单位)。

此视图如“图 51 按近似持续时间排序的 DPC 的被调用方”所示:

图 51 按近似持续时间排序的 DPC 的被调用方

图 51 按近似持续时间排序的 DPC 的被调用方

方法:查看长时间运行的 DPC/ISR

DPC/ISR 的总持续时间很重要,但长时间运行的单个 DPC/ISR 更有可能导致延迟。 在 DPC/ISR 图中,按降序排列的“Inclusive Duration (ms)”列显示单个 DPC/ISR 的最长持续时间。 某些评估配置文件中提供的预设“长 DPC/ISR”允许你筛选此视图,以仅显示非独占持续时间大于 1 毫秒的 DPC/ISR。

注意 如果此预设不可用,可以打开“视图编辑器”的“高级”部分来添加筛选器。

解决方法

DPC/ISR 活动通常反映了必须在硬件或组件级别纠正的硬件或软件问题。 在配置级别,可以替换硬件或将相关驱动程序升级到固定版本。 在组件级别,硬件和驱动程序应遵循 MSDN 中 DPC/ISR 的最佳做法,并且应尽可能使用线程 DPC。 线程 DPC 不在 Windows 客户端版本的调度级别运行。 有关 DPC/ISR 最佳做法的详细信息,请参阅“ISR 和 DPC 行为指南”和“线程 DPC 简介”。

线程 DPC 简介

加载符号

电源管理和 ACPI - 体系结构和驱动程序支持

Windows Vista 和 Windows Server 2008 中的 PPM

计划优先级

计划、线程上下文和 IRQL

Windows Internals Sixth Edition

Windows Performance Analyzer

Windows Performance Toolkit 技术参考