记录 CPU 性能跟踪记录

简介

借助 CPU 性能监控器跟踪提供程序,用户可以通过 Fuchsia 跟踪系统访问 CPU 中内置的性能计数器。

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

在 Intel 上,性能监视器可为用户提供有关 CPU 多个方面的统计信息。有关可用的性能事件的完整列表,例如,Skylake 芯片请参阅 Intel Volume 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 查看器中。

如果您使用的是 traceutil,则可以通过加载 traceutil 生成的相应 .html 文件来更轻松地查看跟踪记录。作者认为从顶级 Fuchsia 目录运行 traceutil、在 Chrome 中查看该目录(例如,file:///home/dje/fnl/ipt/fuchsia).

基本操作

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

跟踪停止时,Cpuperf 跟踪提供程序会读取缓冲区,并将其转换为跟踪管理器使用的跟踪格式。

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

  • 跟踪记录的时长
  • 缓冲区空间
  • 抽样频率
  • 计数器溢出的频率
  • 程序计数器信息是否写入缓冲区

数据收集类别

如前所述,Fucsia 跟踪系统使用“类别”来指定要收集哪些数据。对于 CPU 跟踪,可使用类别指定要启用的计数器、是跟踪操作系统和用户空间,还是跟踪两者,此外还有指定采样率。

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

采样率

每个计数器的数据都按用户指定的速率收集。最终可能会指定一个随机速率。同时,支持以下一组费率:

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

独立抽样

默认情况下,每个计数器都会单独进行采样。例如,如果一个请求以 10, 000 的采样率请求“cpu:fixed:instructions_retired”和“arch:llc”(最后一级缓存 - L3),则每 10000 个“instructionterminate”事件将对已停用的指令进行采样,并且对于前 10,000 个“LLC”拉操作,LLC 操作将频繁采样,且每 10,000 个“LLC”纬度则更为频繁。时间戳是随每个样本一起收集的,因此人们可以知道用了多长时间,例如停用 10000 条指令。

基于时间的抽样

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

请参阅下方内容,了解截至目前为止的时基计数器集;以及当前集的源代码树中的 src/performance/cpuperf_provider/intel-timebase-categories.inc

记录模式

Tally 模式是采样模式的更简单替代方案,在该模式下,系统会在整个跟踪记录运行期间收集每个事件的计数,然后报告。

跟踪模式通过“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 Volume 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 中待处理的非核心需求代码读取交易数量增加每个周期,以取消核心
    • 在季度内,在季度内至少出现 1 次脱离核心的未核对需求代码读取交易的周期
  • cpu:skl:offcore_demand_data

    • 将 SQ 中待处理的非核心需求数据读取交易数量增加每个周期,以取消核心
    • 在季度内,在季度内至少有 1 笔非核心业务的未核准需求数据读取交易的周期
    • 在季度内,在季度内至少有 6 笔非核心的未核对需求数据读取交易的周期
  • cpu:skl:l1_miss_cycles

    • L1 数据错失需求负载未完成时的周期
    • 当 L1 数据缺失需求负载未完成时,执行停止
  • cpu:skl:l2_miss_cycles

    • L2 未命中需求负载时的周期
    • L2 未满足需求负载未处理时,执行停止
  • cpu:skl:l3_miss_cycles

    • L3 未命中需求负载时的周期
    • 第 3 层错失需求负载未处理时,执行停止
  • cpu:skl:mem_cycles

    • 在内存子系统有未完成的负载时循环
    • 当内存子系统有未完成的负载时,执行会停止

时基计数器

这些计数器可以用作时基。 日后我们会陆续添加更多产品。

  • cpu:timebase:fixed:instructions_retired

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

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