Perfetto GPU 追踪完全指南:从 Android 移动图形到多 GPU 高性能计算
【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto
导读
GPU 是复杂软件系统中性能瓶颈的高发地带,从 Android 移动图形渲染到数据中心的多 GPU 计算集群,都需要精准的可观测性支撑。本文以 Perfetto 官方文档 docs/data-sources/gpu.md 为骨架,系统讲解 Perfetto 的 GPU 追踪能力:如何配置gpu.counters、gpu.renderstages、vulkan.memory_tracker等数据源,如何在 Android 上采集 GPU 频率、计数器、显存与渲染阶段,以及如何面向 CUDA/OpenCL/HIP 等 GPGPU 场景做多 GPU、多机、按提交采样的深度分析。读完本文,你将能够独立编写完整的 GPU 追踪配置、理解计数器描述符两种工作模式,并用 Perfetto SQL 与 UI 插件完成内核级性能归因。
一、GPU 追踪全景:数据源与配置速查
Perfetto 通过一组独立的数据源覆盖 GPU 活动的各个方面,每个数据源都有对应的 trace 配置 proto。下表汇总了官方文档给出的全部 GPU 相关数据源:
| 数据源 | 配置 | 用途 |
|---|---|---|
gpu.counters | gpu_counter_config.proto | 周期性或插桩式(instrumented)GPU 计数器采样 |
gpu.renderstages | gpu_renderstages_config.proto | GPU 渲染阶段与计算提交时间线 |
vulkan.memory_tracker | vulkan_memory_config.proto | Vulkan 内存分配与绑定追踪 |
gpu.log | (无配置) | GPU 调试日志消息 |
linux.ftrace | ftrace_config.proto | GPU 频率、显存总量、DRM scheduler 事件 |
1.1 数据源命名:硬件厂商后缀与精确匹配
GPU 生产者(producer)在注册数据源时通常会带上硬件相关的后缀,例如gpu.counters.adreno(高通 Adreno)或gpu.renderstages.mali(ARM Mali)。这带来一个关键约束:
- 追踪服务(tracing service)使用精确名称匹配,因此 trace 配置里的
name必须与被注册的数据源完全一致(含后缀); - trace processor 按 proto 字段类型解析 GPU 数据,因此所有带后缀的变体在解析层是完全等价的——无论数据源叫什么名字,最终都会统一落入 GPU 相关的数据表。
在针对特定 GPU 厂商的生产者时,请在 trace 配置中使用带后缀的名称。最基础的计数器采样配置如下:
data_sources: { config { name: "gpu.counters" gpu_counter_config { counter_period_ns: 1000000 counter_ids: 1 } } }1.2 多 GPU 与多机标识:gpu_id 与 machine_id
所有 GPU 追踪数据都携带两类标识字段:
gpu_id:区分系统内不同的 GPU。在 gpu_counter_event.proto 中,GpuCounterEvent.gpu_id用于多 GPU 设备中标识计数器属于哪块 GPU;GpuRenderStageEvent.gpu_id同理。machine_id:在多机部署架构中区分 GPU 所属的机器。
GPU 硬件元数据(名称、厂商、架构、UUID、PCI BDF)通过 GpuInfo trace packet 记录。从 gpu_info.proto 的源码可以看到,每条Gpu记录包含:
name:GPU 名称(如 "NVIDIA A100"、"Adreno 740"),UI 用它作为 GPU track 的显示标签;多块同型号 GPU 建议追加索引区分(如 "NVIDIA A100 #0");vendor、model、architecture:厂商、型号与架构(如 "Ampere"、"RDNA 3");uuid:16 字节设备 UUID;pci_bdf:PCI 总线位置(如 "0000:01:00.0");extra_info:任意键值对,用于驱动版本、显存大小、计算能力等厂商自定义信息。
GpuInfo.gpus列表的索引即对应全 trace 中使用的gpu_id,这是 UI 正确把 track 挂到对应 GPU 下的前提。
二、Android GPU 追踪实践
Android 是 Perfetto GPU 追踪最成熟的平台,官方文档按频率、计数器、显存、渲染阶段、Vulkan 内存、GPU 日志六个维度给出了可直接落地的配置。
2.1 GPU 频率(ftrace)
GPU 频率通过 ftrace 事件power/gpu_frequency采集:
data_sources: { config { name: "linux.ftrace" ftrace_config { ftrace_events: "power/gpu_frequency" } } }2.2 GPU 计数器(counter descriptor mode 1)
Android GPU 生产者必须使用计数器描述符模式 1(counter descriptor mode 1):GpuCounterDescriptor直接嵌入在会话的第一个GpuCounterEventpacket 中,且 counter ID 是全局的。这是 CDD/CTS 合规的硬性要求——从 gpu_counter_event.proto 的注释可见:mode 1 要求 counter id 全局唯一,因此只能由单个生产者发出,或需要不同生产者之间协调;这正是 Android OEM 为满足 CDD/CTS 测试必须采用的机制。
计数器按设备相关的 ID 采样,可用的 counter ID 由数据源描述符中的GpuCounterSpec描述。示例:
data_sources: { config { name: "gpu.counters" gpu_counter_config { counter_period_ns: 1000000 counter_ids: 1 counter_ids: 3 counter_ids: 106 counter_ids: 107 counter_ids: 109 } } }其中counter_period_ns设置期望的采样间隔。从 gpu_counter_config.proto 源码看,该配置还支持一个容易被忽略的字段fix_gpu_clock:在 trace 会话期间固定 GPU 时钟频率,用于保证采样数据可比性。
按名称选择计数器:除了counter_ids,还可以用counter_names按名称选计数器(配置语义上两者互斥,只用其一)。注意:
- 并非所有生产者都支持按名称选择——需要检查数据源描述符中
GpuCounterDescriptor.supports_counter_names; counter_names支持 glob 通配模式批量匹配,但同样需要检查supports_counter_name_globs支持标志。
从 gpu_counter_descriptor.proto 源码可以看到这两个能力标志,以及 Android GPU 生产者通常不支持按名称选择(注释明确写道 "Android GPU producers typically do not")。
2.3 GPU 显存(ftrace)
每个进程的 GPU 显存总用量通过 ftrace 事件gpu_mem/gpu_mem_total采集:
data_sources: { config { name: "linux.ftrace" ftrace_config { ftrace_events: "gpu_mem/gpu_mem_total" } } }2.4 GPU 渲染阶段(render stages)
渲染阶段追踪提供 GPU 活动时间线(图形与计算提交):
data_sources: { config { name: "gpu.renderstages" } }若需要更精细的控制,gpu_renderstages_config.proto 还提供三个可选字段:
full_loadstore:把颜色与深度/模板(depth/stencil)的 load 与 store 阶段拆分为独立阶段;关闭时二者合并。默认关闭,且在低开销模式下无效;low_overhead:低开销模式,所有渲染阶段合并为单一 workload 阶段,数据粒度变粗但 GPU 开销最小。默认关闭;trace_metrics:要为每个渲染阶段采集的指标列表。
2.5 Vulkan 内存追踪
Vulkan 内存分配与绑定事件可通过vulkan.memory_tracker追踪:
data_sources: { config { name: "vulkan.memory_tracker" vulkan_memory_config { track_driver_memory_usage: true track_device_memory_usage: true } } }vulkan_memory_config.proto 定义了两个开关:track_driver_memory_usage追踪驱动侧内存(host 内存)使用事件,track_device_memory_usage追踪设备侧(显存)内存使用事件。
2.6 GPU 日志
GPU 调试日志消息可直接启用数据源采集,无需额外配置:
data_sources: { config { name: "gpu.log" } }三、高端 GPGPU:多 GPU 与多机追踪
针对高性能与数据中心 GPU 负载(CUDA、OpenCL、HIP),Perfetto 支持多 GPU、多机追踪与插桩式计数器采样。文档中明确说明:Perfetto 的定位是客户端侧追踪(见 docs/README.md 中对使用边界的描述),GPU 渲染阶段与计数器记录在 Android 上已有成熟支持,而游戏领域更专业的 GPU 分析工具是 Android GPU Inspector(其底层也以 Perfetto 作为数据源之一)。
3.1 插桩式计数器采样(Instrumented Counter Sampling)
与全局周期采样不同,插桩式采样通过改写 GPU 命令缓冲区来采集计数器,能够获得**每次提交(per-submission)**的计数器值:
data_sources: { config { name: "gpu.counters" gpu_counter_config { counter_ids: 1 counter_ids: 2 instrumented_sampling: true } } }instrumented_sampling是布尔开关,与instrumented_sampling_config构成 oneof 二选一(见 gpu_counter_config.proto 中的instrumented_sampling_mode)。需要精确控制插桩哪些 GPU 活动时,使用instrumented_sampling_config,它定义了一个按顺序执行的三级过滤管线:
第 1 步:活动名称过滤(Activity name filtering)。若activity_name_filters非空,则活动必须至少匹配一个过滤器。每个过滤器由name_glob(必需,glob 模式)与name_base(可选,决定匹配基准)组成。name_base的三种取值定义在 proto 的ActivityNameFilter.NameBase枚举中:
| 取值 | 匹配对象 | 示例 |
|---|---|---|
MANGLED_KERNEL_NAME(默认) | 编译期编码的(mangled)内核名 | _Z6matmulPfii |
DEMANGLED_KERNEL_NAME | 完整反修饰内核名(含参数、模板、限定符) | matmul(float*,int,int) |
FUNCTION_NAME | 仅反修饰后的裸函数名 | matmul |
若activity_name_filters为空,所有活动直接通过本步骤。
第 2 步:TX 范围过滤(TX range filtering)。TX 范围是进程内的注解,用于标记 GPU 工作区段(如 CUDA 的 NVTX range)。若activity_tx_include_globs非空,活动必须落在匹配某个 include glob 的 TX 范围内;落在匹配activity_tx_exclude_globs范围内的活动被排除(exclude 优先于 include)。TX 范围可嵌套,只要活动嵌套层级中的任一范围匹配即视为匹配。两者都为空时所有活动通过。
第 3 步:范围采样(Range-based sampling)。若activity_ranges非空,只插桩指定 skip/count 范围内的活动。skip默认 0,count默认UINT32_MAX(即剩余全部活动)。为空时,通过前两步的所有活动都会被插桩。
官方给出的综合示例——只插桩反修饰内核名匹配"myKernel*"、且位于匹配"training*"的 TX 范围内、跳过前 10 个活动再插桩 5 个:
data_sources: { config { name: "gpu.counters" gpu_counter_config { counter_names: "sm__cycles_elapsed.avg" counter_names: "sm__cycles_active.avg" instrumented_sampling_config { activity_name_filters { name_glob: "myKernel*" name_base: DEMANGLED_KERNEL_NAME } activity_tx_include_globs: "training*" activity_ranges { skip: 10 count: 5 } } } } }3.2 计数器描述符模式 2:GPGPU 场景的推荐选择
对 GPGPU 用例,官方推荐计数器描述符模式 2:生产者发出由 IID 引用的InternedGpuCounterDescriptor,使每个可信序列(trusted sequence)拥有自己作用域内的 counter ID。这与模式 1 形成鲜明对比:
- 模式 1:
GpuCounterDescriptor内嵌在首个GpuCounterEvent中,counter ID 全局唯一,要求单生产者或跨生产者协调,多生产者、多 GPU 场景难以扩展; - 模式 2:
GpuCounterEvent通过counter_descriptor_iid引用InternedData中的InternedGpuCounterDescriptor,天然支持多生产者与多 GPU。
两种模式的完整字段定义见 gpu_counter_event.proto。特别地,InternedGpuCounterDescriptor中也有自己的gpu_id,且其优先级高于GpuCounterEvent.gpu_id字段。
计数器的名称与 ID 由 GPU 生产者通过数据源描述符中的GpuCounterSpec对外发布。gpu_counter_descriptor.proto 显示GpuCounterSpec还携带丰富的元数据:description、numerator_units/denominator_units(测量单位,如 HERTZ、BYTE、PERCENT、WATT、CELSIUS 等MeasureUnit枚举)、int_peak_value/double_peak_value(峰值)、select_by_default,以及value_direction——该字段告知 trace processor 采样值作用于时间戳的哪一侧区间(VALUE_DIRECTION_BACKWARDS_LOOKING表示值覆盖到该时间戳为止的区间,VALUE_DIRECTION_FORWARDS_LOOKING表示从该时间戳开始的区间),直接影响 UI 中计数器的绘制位置。
3.3 计数器分组(Counter Groups)
计数器分组用于让 Perfetto UI 把计数器轨道组织成组。计数器可以归属内置分组(SYSTEM、VERTICES、FRAGMENTS、PRIMITIVES、MEMORY、COMPUTE、RAY_TRACING,即GpuCounterGroup枚举,见 gpu_counter_descriptor.proto),通过GpuCounterSpec.groups指定。生产者还可以用GpuCounterDescriptor中的GpuCounterGroupSpec消息定义自定义分组:
message GpuCounterGroupSpec { optional uint32 group_id = 1; optional string name = 2; optional string description = 3; repeated uint32 counter_ids = 4; }自定义分组有两个用途:
- 定义硬件相关的自定义分组(如 "Compute Core"、"L2 Cache"),UI 据此在
Counters下生成可折叠子分组; - 为固定的
GpuCounterGroup枚举值提供显示名称与描述——将group_id设为枚举值,并提供name和/或description即可。
计数器的分组归属是并集:GpuCounterSpec.groups(固定枚举)与GpuCounterGroupSpec.counter_ids(自定义分组)指派的分组共同生效。例如配置了自定义分组 "Compute Core" 与 "L2 Cache" 后,UI 中的轨道组织方式为:
GPU > Counters > Compute Core > Counter A GPU > Counters > Compute Core > Counter B GPU > Counters > L2 Cache > Counter C3.4 多 GPU(Multi-GPU)
系统中的每块 GPU 都被分配一个gpu_id。计数器事件、渲染阶段等 GPU 追踪数据都携带该 ID,UI 据此按 GPU 分组轨道。GPU 硬件细节通过 GpuInfo 记录,包含前文所述的name、vendor、model、architecture、uuid(16 字节)与pci_bdf(PCI 总线/设备/功能号)。
3.5 多机(Multi-Machine)
跨机器追踪时,每个 GPU 追踪事件还携带machine_id以区分 GPU 所属机器,Perfetto UI 会在 GPU 轨道旁显示机器标签。多机架构的完整方案见多机部署架构文档。
3.6 渲染阶段事件关联(Render Stage Event Correlation)
GPU 渲染阶段事件可以通过GpuRenderStageEvent上的event_wait_ids字段声明对其他渲染阶段事件的依赖。gpu_render_stage_event.proto 将event_wait_ids定义为"该事件运行前必须等待的其他GpuRenderStageEvent的event_id列表"。trace processor 会利用这些依赖在被关联的 GPU slice 之间创建 flow 箭头。
官方示例:一个 matmul 内核依赖一次异步 memcpy:
gpu_render_stage_event { event_id: 1 duration: 50000 hw_queue_iid: 1 stage_iid: 2 context: 0 name: "Memcpy HtoD" } gpu_render_stage_event { event_id: 2 duration: 40000 hw_queue_iid: 3 stage_iid: 4 context: 0 name: "matmul_kernel" event_wait_ids: 1 }这会在 memcpy 事件(event_id 1)与 matmul 内核(event_id 2)之间创建一条 flow,在 Perfetto UI 中直观呈现依赖关系。
此外,gpu_render_stage_event.proto 中还定义了面向计算场景的丰富字段:kernel_iid(引用 interned 的计算内核描述InternedComputeKernel,含demangled_name、arch与类型化属性)、launch(ComputeKernelLaunch,记录 grid/workgroup 三维尺寸与寄存器数、共享内存等类型化启动参数)、extra_data(用户自定义键值对,可携带资源 ID、着色器等)。InternedGpuRenderStageSpecification.category枚举(OTHER/GRAPHICS/COMPUTE)则是后续 SQL 查询中区分计算内核与图形事件的基础。
3.7 主机到 GPU 关联(Host-to-GPU Correlation)
主机侧 track event 可以使用GpuCorrelationTrackEvent 扩展与 GPU 渲染阶段事件关联,用于把主机 API 调用(如cudaLaunchKernel、cudaMemcpyAsync)与对应的 GPU 工作连接起来。扩展定义在 gpu_track_event.proto 中,提供两个字段:
render_stage_submission_event_ids:该主机事件提交的 GPU 渲染阶段事件的 event ID;render_stage_wait_event_ids:该主机事件等待其完成的 GPU 渲染阶段事件的 event ID。
官方示例——主机内核启动与 GPU 计算内核的关联:
track_event { type: TYPE_SLICE_BEGIN name: "cudaLaunchKernel" [perfetto.protos.GpuTrackEvent.gpu_correlation] { render_stage_submission_event_ids: 1 } } gpu_render_stage_event { event_id: 1 duration: 50000 hw_queue_iid: 1 stage_iid: 2 context: 0 name: "matmul_kernel" }同一 proto 还定义了GpuApi枚举(GPU_API_OPEN_GL、GPU_API_VULKAN、GPU_API_OPEN_CL、GPU_API_CUDA、GPU_API_HIP),其取值与InternedGraphicsContext.Api刻意保持一致,可用于标记 track event 对应的 GPU API,UI 据此按 API 组织轨道。
四、UI 插件:GPU 数据的可视化层
Perfetto UI 内置了多个消费 GPU 追踪数据的插件。它们在 workspace 树的GPU分组下注册轨道、分组与详情面板(per-process 插件则注册到各进程分组下)。源码位置在 ui/src/plugins 目录,默认插件清单见 ui/src/core/embedder/default_plugins.ts。
4.1 dev.perfetto.Gpu
基础插件,为每块 GPU 布局一个GPU分组,并为gpu_counter_track、gpu_render_stage、gpu_log、vulkan_events、graphics_frame_event各族的全部内容填充叶子轨道与汇总轨道:
- 多 GPU/多机:拆分为按 GPU 的子分组(存在多台机器时附加机器标签);
- 自定义计数器分组:
GpuCounterDescriptor/GpuCounterGroupSpec声明的自定义分组在Counters下显示为可折叠子分组。
4.2 dev.perfetto.GpuByProcess
该插件展示仅作用于单个进程、没有全局意义的 GPU 概念。典型例子是 CUDA stream:它是进程级句柄,两个进程里相同的数值streamID 指向两个互不相关的流,因此把所有流堆在共享的GPU分组下会产生误导。此插件将这些轨道放到各自所属进程之下。
对携带device与stream启动参数(如 CUDA、HIP)的 GPU slice,插件按<API> → Device #N → Context #N → Stream #N在进程下嵌套组织gpu_render_stageslice,只有单一取值的层级会被折叠;不带这些参数的 slice 回退为按hw_queue_id每个硬件队列一条轨道(通常命名为"Channel #N")。当进程跨多块 GPU 时,叶子轨道再按 GPU 子分组嵌套。
4.3 com.meta.GpuCompute
计算内核深度分析插件(对应源码目录 ui/src/plugins/com.meta.GpuCompute)。当选中计算类gpu_render_stageslice(即gpu_slice.render_stage_category = COMPUTE)时,提供三个标签页:
- Summary(汇总):trace 中每次内核启动的表格,可按耗时、占用率(occupancy)等硬件指标排序;双击跳转到该内核的详情视图;
- Details(详情):分节指标表(Speed-of-Light、Launch Statistics、Occupancy、Compute Workload Analysis),支持两个内核之间的基线对比;
- Toolbar(工具栏):内核选择器、基线固定、术语切换(CUDA / OpenCL / 厂商自定义)以及自动单位换算(bytes → KB、ns → s 等)。
核心插件自带 CUDA 与 AMD 支持;其他厂商通过配套插件注册术语、指标分节、知名指标 ID 与分析提供者来扩展(扩展 API 见该插件目录下的 README)。
五、示例查询:用 Perfetto SQL 做 GPU 分析
GPU 追踪数据最终落在 trace processor 的 SQL 表中(gpu_counter_track、gpu_slice、gpu_track、gpu等),可以用 Perfetto SQL 分析文档 描述的标准 SQL 语法做深度分析。官方文档给出一个非常实用的查询示例:找出耗时最长的 5 个内核,并计算每个内核执行窗口内的时间加权 GPU 利用率。
查询的核心技巧在于两个 Perfetto 标准库模块(counters.intervals 相关模块在src/trace_processor/perfetto_sql/stdlib目录下可查,仓库中android/gpu/frequency.sql等大量 SQL 均使用了counter_leading_intervals):
counter_leading_intervals:把稀疏的计数器采样展开为(ts, dur, value)区间(每个采样值持续到下一个采样);_interval_intersect:把这些区间与每个内核的[ts, ts + dur)窗口求交集,从而让平均值真正按"每个计数器值在内核期间实际生效的时长"加权。
INCLUDE PERFETTO MODULE counters.intervals; INCLUDE PERFETTO MODULE intervals.intersect; WITH -- The GPU Utilization counter, expanded into (ts, dur, value) intervals. -- Carries ugpu so the intersect can match each kernel to its own GPU. utilization AS ( SELECT u.id, u.ts, u.dur, u.value, gct.ugpu FROM counter_leading_intervals!(( SELECT c.id, c.ts, c.track_id, c.value FROM counter c JOIN gpu_counter_track gct ON gct.id = c.track_id WHERE gct.name = 'Utilization' )) u JOIN gpu_counter_track gct ON gct.id = u.track_id ), -- The 5 longest compute kernels (render_stage_category 2 = COMPUTE). top_kernels AS ( SELECT s.id, s.ts, s.dur, s.name, extract_arg(t.dimension_arg_set_id, 'ugpu') AS ugpu FROM gpu_slice s JOIN gpu_track t ON s.track_id = t.id WHERE s.render_stage_category = 2 AND s.dur > 0 ORDER BY s.dur DESC LIMIT 5 ) SELECT k.name AS kernel, g.name AS gpu_name, k.dur AS dur_ns, -- Time-weighted average: sum(value * overlap_dur) / kernel_dur. SUM(u.value * ii.dur) / k.dur AS avg_utilization FROM top_kernels k LEFT JOIN gpu g ON g.id = k.ugpu JOIN _interval_intersect!((top_kernels, utilization), (ugpu)) ii ON ii.id_0 = k.id JOIN utilization u ON u.id = ii.id_1 GROUP BY k.id, k.name, g.name, k.dur ORDER BY k.dur DESC;几个值得注意的实现细节:
- 内核的
ugpu通过extract_arg(t.dimension_arg_set_id, 'ugpu')从 GPU track 的维度参数中取出,用于在 intersect 时把每个内核只与它自己的 GPU的利用率计数器匹配(_interval_intersect的第二个参数(ugpu)就是关联键); render_stage_category = 2对应InternedGpuRenderStageSpecification.RenderStageCategory.COMPUTE(OTHER=0、GRAPHICS=1、COMPUTE=2,见 gpu_render_stage_event.proto);- 时间加权平均的计算方式是
SUM(值 × 区间重叠时长) / 内核总时长。
示例输出(双 GPU 训练 trace):
| kernel | gpu_name | dur_ns | avg_utilization |
|---|---|---|---|
| matmul_bwd_kernel | NVIDIA A100-SXM4-80GB #1 | 180000 | 78.27 |
| matmul_bwd_kernel | NVIDIA A100-SXM4-80GB #2 | 180000 | 77.25 |
| matmul_kernel | NVIDIA A100-SXM4-80GB #1 | 125000 | 78.70 |
| matmul_kernel | NVIDIA A100-SXM4-80GB #2 | 125000 | 78.83 |
| softmax_bwd_kernel | NVIDIA A100-SXM4-80GB #1 | 110000 | 73.76 |
这个查询直观展示了如何把"计数器采样"与"内核 slice"两类异构数据在 SQL 层对齐,进而量化每个内核的 GPU 利用率,是排查计算内核性能瓶颈的起点。
六、进一步阅读
- 在 Android 上采集系统 trace 的完整流程:Android 系统追踪入门
- GPU 数据的底层 proto 定义:gpu_counter_event.proto、gpu_render_stage_event.proto、gpu_track_event.proto
- 计数器描述符与能力协商:gpu_counter_descriptor.proto
- 多机追踪架构:多机部署架构
- Perfetto SQL 语法与标准库:Perfetto SQL 语法、Perfetto SQL 快速上手
【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考