记录 CPU 性能跟踪记录

简介

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

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

在 Intel 上,性能监视器可向用户提供有关 CPU 的许多方面统计信息。如需查看可用于(例如Skylake 芯片:请参阅 Intel 第 3 卷第 19.2 章“第 6 代和第 7 代处理器的性能监控事件”。目前并非所有事件(或“计数器”)都已推出,但我们已经推出了许多事件,希望其中有一些对您有用。

示例如下:

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

跟踪系统使用“类别”来让用户指定要收集哪些轨迹数据。Cpuperf 使用这些类别来简化要启用的硬件事件的规范。您可以在该目录中的 .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 中断(Performance Monitor 中断)。在 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

独立采样

默认情况下,系统会独立对每个计数器进行抽样。例如,如果请求“cpu:fixed:instructions_retired”和“arch:llc”(最后一级缓存 - L3),并将采样率设为 10000,则系统将每 10000 个“instruction retired”事件采样一次已退出的指令,并每 10000 个“LLC”事件采样一次 LLC 操作,前者发生的频率远高于后者。系统会收集每个样本的时间戳,这样就可以知道样本所用的时间,例如,淘汰了 10000 条指令。

基于时间的采样

有几个计数器可用作“时间基准”。 在时间基准模式下,一个计数器用于驱动所有计数器的数据收集,而不是每个计数器以自己的速率收集数据。这样可以更一致地了解正在发生的情况。另一方面,这样做意味着我们会放弃收集每个事件的统计 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 中核心未处理需求数据读取事务的数量,以取消核心
    • 在 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 相同