记录 CPU 性能跟踪记录

简介

CPU 性能监视器跟踪提供程序可让用户使用 Fuchsia 跟踪系统访问内置于 CPU 中的性能计数器。

目前,只有 Intel 芯片组支持此功能。

在 Intel 上,性能监视器可向用户提供有关 CPU 的许多方面统计信息。如需查看可用于(例如Skylake 芯片:请参阅 Intel 第 3 卷第 19.2 章“第 6 代和第 7 代处理器的性能监控事件”。目前并非所有事件(或“计数器”)都可用,有很多事件可供选择,但希望当前存在许多有用的事件。

示例如下:

  • 每个 L1、L2、L3 的缓存命中/未命中次数
  • 因缓存未命中而停止的周期
  • 分支错误预测
  • 已弃用说明

跟踪系统使用“类别”来让用户指定要收集哪些轨迹数据。Cpuperf 使用这些类别来简化指定要启用的 H/W 事件的规范。您可以在该目录中的 .inc 文件中找到完整的类别。下面介绍了一些代表性的类别。

如需收集轨迹数据,请在宿主机上运行 ffx trace start,或直接在 Fuchsia 设备上运行 trace

示例:

$ categories="gfx"
$ categories="$categories,cpu:fixed:unhalted_reference_cycles"
$ categories="$categories,cpu:fixed:instructions_retired"
$ categories="$categories,cpu:l2_lines,cpu:sample:10000"
$ ffx trace start --buffer-size 64 --duration 2 --categories $categories

.fxt 文件保存到桌面后,您可以将其加载到 Perfetto 查看器中。

基本操作

性能数据收集的基本操作是,为每个 CPU 分配一个跟踪记录缓冲区,然后设置一个计数器(在每个 CPU 上),以便在发生预指定数量的事件后触发中断。此中断称为 PMI 中断(性能监视器中断)。在 Intel 上,当计数器溢出时会触发中断,此时中断服务例程会将各种信息(例如时间戳和程序计数器)写入跟踪缓冲区,重置计数器以便在预先指定数量的事件后重新触发另一个中断,然后返回。

跟踪停止时,Cpuperf Trace Provider 会读取缓冲区,并将其转换为 Trace Manager 使用的轨迹格式。

当缓冲区填满时,跟踪也会停止。请注意,系统会使用内部缓冲区,因此(目前)不支持循环模式和流式传输模式。可收集的轨迹数据量取决于多种因素:

  • 轨迹时长
  • 缓冲区空间
  • 采样频率
  • 计数器溢出的频率
  • 是否将程序计数器信息写入缓冲区

数据收集类别

如前所述,Fuchsia 跟踪系统使用“类别”来让用户指定要收集的数据。对于 CPU 跟踪,您可以使用类别来指定要启用哪些计数器、是否跟踪操作系统和/或用户空间,以及指定采样频率。

如需详细了解每个性能计数器,请参阅 Intel 文档。本文档不会尝试提供有关每个计数器的详细信息。

采样率

系统会按照用户指定的速率收集每个计数器的数据。最终,我们将能够指定随机费率。在此期间,我们支持以下一组费率:

  • cpu:sample:100
  • cpu:sample:500
  • cpu:sample:1000
  • cpu:sample:5000
  • cpu:sample:10000
  • cpu:sample:50000
  • cpu:sample:100000
  • cpu:sample:500000
  • cpu:sample:1000000

独立采样

默认情况下,系统会独立对每个计数器进行抽样。例如,如果以 10000 的采样率请求“cpu:fixed:instructions_retired”和“arch:llc”(最后一级缓存 - L3),系统将每隔 10000 个“指令停用”事件对已停用的指令进行采样,并且对于前者,每 10000 个“LLC”事件的采样频率将高于前者。系统会随每个样本收集时间戳,以便用户了解执行某项操作(例如退回 10,000 条指令)所花费的时间。

基于时间的采样

有一些计数器可用作“时基”。在时基模式下,一个计数器用于驱动所有计数器的数据收集,而不是按自己的速率收集每个计数器。这样可以更一致地了解正在发生的情况。另一方面,这样做意味着我们会放弃收集每个事件的统计 PC 数据(因为我们将获得的唯一 PC 值是基准时间事件的 PC 值)。除了时间基准计数器之外,还必须提供采样率。

请参阅下文,了解本文撰写时的一组时间基准计数器,以及当前集的源代码树中的 src/performance/cpuperf_provider/intel-timebase-categories.inc

统计模式

统计模式是采样模式的更简单替代方案,它会在整个轨迹运行期间收集每个事件的计数,然后进行报告。

通过“cpu:tally”类别(而非“cpu:sample:*”类别)启用统计模式。

示例:

$ categories="cpu:l2_summary"
$ categories="$categories,cpu:fixed:unhalted_reference_cycles"
$ categories="$categories,cpu:fixed:instructions_retired"
$ categories="$categories,cpu:mem:bytes,cpu:mem:requests"
$ categories="$categories,cpu:tally"
$ ffx trace start --buffer-size 64 --duration 2 --categories $categories

选项

  • cpu:os - 收集在内核空间中运行的代码的数据。

  • cpu:user - 收集在用户空间中运行的代码的数据。

  • cpu:pc - 收集与每个事件关联的 PC 数据

例如,如果您想知道缓存缺失通常发生在哪里(从统计学角度而言,取决于抽样率),这会很有用。轨迹输出中包含每个采样点的地址空间和程序计数器。不过,这样做会使每个轨迹记录的大小翻倍,因此需要权衡利弊。

固定计数器

Intel 架构提供了三个“固定”计数器:

  • cpu:fixed:instructions_retired

  • cpu:fixed:unhalted_core_cycles

  • cpu:fixed:unhalted_reference_cycles

这些计数器是“固定”的,因为它们不使用可编程计数器。其中有三个,每个都有固定用途。它们的优点是不会耗尽一个可编程计数器:有数十个计数器,但根据模型,通常一次最多只能使用四个计数器。

可编程计数器

Skylake(和 Kaby Lake)芯片上有数十个可编程计数器。如需查看完整列表,请参阅 Intel 第 3 卷第 19.2 章“第 6 代和第 7 代处理器的性能监控事件”。如需查看目前支持的平台列表,请参阅源代码树中的 zircon/system/ulib/zircon-internal/include/lib/zircon-internal/device/cpu-trace/intel-pm-events.inczircon/system/ulib/zircon-internal/include/lib/zircon-internal/device/cpu-trace/skylake-pm-events.inc

为了简化指定可编程计数器的过程,在源代码树中,我们将可编程计数器划分到了 src/performance/cpuperf_provider/intel-pm-categories.incsrc/performance/cpuperf_provider/skylake-pm-categories.inc 中定义的类别。如需查看完整列表,请参阅这些文件。

一次只能指定其中一个类别。[稍后,我们将提供更多数据收集控制选项。]

以下是一些实用类别:

  • cpu:arch:llc

    • 最后一级缓存 (L3) 引用
    • 最后一级缓存 (L3) 未命中
  • cpu:arch:branch

    • 分支指令已停用
    • 分支指令预测错误
  • cpu:skl:l1_summary

    • 每个周期的未处理 L1D 缺失次数
    • 此处理器核心上任何逻辑线程的未处理 L1D 缺失次数
    • 提取到 L1 数据缓存的行数
  • cpu:skl:l2_summary

    • 错失 L2 的需求请求
    • 所有未命中 L2 的请求
    • 对 L2 的所有需求数据读取请求
    • 对 L2 的所有请求
  • cpu:skl:l3_summary

    • 来自引用 L3 中缓存行的核心的请求
    • 对 L3 的引用的缓存未命中条件
  • cpu:skl:offcore_demand_code

    • 将 SQ 中未处理的离线需求代码读取事务的数量在每个周期递增到 uncore
    • 在 SQ 中至少有 1 个非核心待处理需求代码读取事务的周期
  • cpu:skl:offcore_demand_data

    • 将 SQ 中每个周期的离核心未处理需求数据读取事务数量递增到 uncore
    • 在 SQ 中至少有 1 个未处理的离核心需求数据读取事务的周期
    • 在 SQ 中至少有 6 个未处理的离核心需求数据读取事务的周期
  • cpu:skl:l1_miss_cycles

    • 在 L1 数据缺失需求加载时所经历的周期
    • 在 L1 数据缺失需求加载待处理时,执行会暂停
  • cpu:skl:l2_miss_cycles

    • L2 缺失需求载荷待处理时的周期
    • 在 L2 缺失需求加载未处理完毕时,执行会暂停
  • cpu:skl:l3_miss_cycles

    • L3 缺失需求载荷未处理时的周期
    • 在 L3 缺失需求加载未处理完毕时,执行会暂停
  • cpu:skl:mem_cycles

    • 内存子系统有未处理的负载时的周期
    • 当内存子系统有未完成的负载时,执行会停止

时间基准计数器

这些计数器可用作时间基准。我们会及时添加更多产品。

  • cpu:timebase:fixed:instructions_retired

    • 计数器与 cpu:fixed:instructions_retired 相同
  • cpu:timebase:fixed:unhalted_reference_cycles

    • 与 cpu:fixed:unhalted_reference_cycles 相同的计数器