Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


title: Perfetto CPU 频率与 DVFS 关联分析 chapter: '14.9' section: '14.9' status: finalized applicable_versions: Android 9 (API 28) - Android 17 (API 37) last_verified: '2026-08-13' last_verified_against: Android 17 / API 37 / AOSP android-17.0.0_r1 Perfetto sources; current Perfetto CPU frequency docs and PerfettoSQL linux.cpu.frequency/idle reference checked 2026-08-13; Android common kernel android17-6.18-2026-06_r6 CPUFreq/CPUIdle sources; arXiv 2507.02135v1 with explicit Android 13 Pixel/Tensor G2 boundary last_rework_at: '2026-08-07T21:35:15+08:00' last_rework_run_id: 20260807-213515-rework-952e31cf confidence: medium-high tags:

  • perfetto
  • cpu-frequency
  • dvfs
  • power
  • scheduling related_chapters:
  • '5.1'
  • '5.2'
  • '11.1'
  • '14.3' sources:
  • type: source path: https://android.googlesource.com/platform/external/perfetto/+/refs/tags/android-17.0.0_r1/docs/data-sources/cpu-freq.md
  • type: source path: https://android.googlesource.com/platform/external/perfetto/+/refs/tags/android-17.0.0_r1/src/traced/probes/common/cpu_freq_info.cc
  • type: source path: https://android.googlesource.com/platform/external/perfetto/+/refs/tags/android-17.0.0_r1/src/trace_processor/perfetto_sql/stdlib/linux/cpu/frequency.sql
  • type: source path: https://android.googlesource.com/platform/external/perfetto/+/refs/tags/android-17.0.0_r1/src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle.sql
  • type: source path: https://android.googlesource.com/platform/external/perfetto/+/refs/tags/android-17.0.0_r1/src/trace_processor/tables/metadata_tables.py
  • type: source path: https://android.googlesource.com/platform/packages/modules/Profiling/+/refs/tags/android-17.0.0_r1/service/java/com/android/os/profiling/Configs.java
  • type: source path: https://android.googlesource.com/kernel/common/+/refs/tags/android17-6.18-2026-06_r6/kernel/sched/cpufreq_schedutil.c
  • type: source path: https://android.googlesource.com/kernel/common/+/refs/tags/android17-6.18-2026-06_r6/Documentation/admin-guide/pm/cpufreq.rst
  • type: source path: https://android.googlesource.com/kernel/common/+/refs/tags/android17-6.18-2026-06_r6/Documentation/admin-guide/pm/cpuidle.rst
  • type: paper path: https://arxiv.org/abs/2507.02135
  • type: obsidian path: Cubox/Perfetto查看CPU 频率部分指导-2026-05-03.md
  • type: obsidian path: 论文/Android-2026-05-15-DVFS-LLM-Performance/03-精读.md pipeline_stage: ready-to-publish task6_state: reviewed last_review_finalize_at: '2026-08-08T14:05:39+08:00' last_review_finalize_run_id: 20260808-140539-40d25f76 task9_state: reviewed task2b_state: fixed

Perfetto CPU 频率与 DVFS 关联分析

本文讨论 DVFS(Dynamic Voltage and Frequency Scaling,动态电压与频率调节)时,以 Android 17 / API 37 / android-17.0.0_r1 为平台版本,以 android17-6.18-2026-06_r6 为内核版本。Perfetto 的 CPU Frequency 轨道记录软件可见的频率状态,线程轨道记录调度状态。把两者放进同一时间窗,才能回答“线程处于 Running 状态时,所在 CPU 报告了什么频率”。

这条证据有明确限制:频率轨并非直接测量硬件时钟。Android 17 内核在 cpufreq 普通切换完成后,把 freqs->new 写入 cpu_frequency;快速切换路径写入驱动返回的 freq。同一 cpufreq policy(共享一套性能控制接口的一组 CPU)内的在线 CPU 会分别产生事件。linux.sys_stats 轮询的也是 sysfs(Linux 向用户空间暴露内核状态的虚拟文件接口)中的值。仅凭这些数据,不能断言某段代码获得了对应数量的有效执行周期。

可复核来源分成三层:Android 17 tag 下的 Perfetto 采集、Trace Processor 表和 ProfilingManager 源码;android17-6.18-2026-06_r6 下的 CPUFreq、CPUIdle 与 schedutil 内核源码/文档;以及端侧 LLM(Large Language Model,大语言模型)DVFS 论文中限定在 Pixel 7 / Pixel 7 Pro、Android 13、Tensor G2 和特定推理栈内的实验结果。第三层只用于观察多个硬件域的 governor 是否协同,论文数字不能外推成 Android 17 或其他 SoC 的通用结论。

频率轨记录的是什么

Linux CPUFreq 调频框架把控制路径分成 policy、governor 和 scaling driver:

  • policy 表示一组共享硬件性能控制接口的 CPU,并维护允许的最小、最大频率与当前频率;
  • governor 根据利用率、时序约束等信息提出目标频率;
  • scaling driver 把目标提交给硬件,硬件仍可能受固件、温控或内部控制影响。

Android 17 的 schedutil governor 以利用率和 CPU capacity(相对算力)计算候选频率,再交给 cpufreq_driver_resolve_freq() 选择驱动支持的频点。源码还保留 Android vendor hook(供厂商插入定制逻辑的钩子),厂商可以改写映射结果。默认更新间隔来自 cpufreq_policy_transition_delay_us(policy),不存在跨设备通用的固定调频周期。Android 17 schedutil 源码

频率证据可以按下面的强弱理解:

数据能证明什么不能单独证明什么
power/cpu_frequencycpufreq 路径报告了一次频率切换硬件在整个区间都精确运行于该频率
linux.sys_stats轮询时刻的 sysfs 频率值两次轮询之间的瞬时切换
power/cpu_frequency_limitspolicy 的最小、最大频率约束发生更新约束由 thermal、Power HAL 还是厂商逻辑触发
cpuinfo_cur_freq / cpuinfo_avg_freq驱动具备相应反馈能力时,更接近硬件观测所有设备都实现并允许读取
PMU(Performance Monitoring Unit,性能监控单元)/ AMU(Activity Monitor Unit,活动监控单元)与硬件功耗计实际执行、周期或能耗的补充证据单靠 Perfetto cpufreq 轨无法替代这些数据

Android 17 的 Perfetto 文档和 proto(Protocol Buffers 配置定义)注释把轮询路径描述为读取 cpuinfo_cur_freq,同一版本的 CpuFreqInfo::ReadCpuCurrFreq() 实现读取的是 scaling_cur_freq。内核文档指出,scaling_cur_freq 多数情况下表示 scaling driver 最近请求的 P-state(处理器性能状态),未必等于硬件瞬时频率。核对某一版本时应以该版本实现为准,同时保留文档与实现的差异。Android 17 Perfetto 采集实现 Android 17 CPUFreq 文档

采集入口:事件、轮询和系统信息

下面的配置同时采集频率切换、频率限制、空闲状态、调度事件、轮询频率和可用频点。500 ms 来自 Android 17 Perfetto 官方示例,只用于补充初始频率和观察慢变化;它不适合还原单帧内的调频过程。

data_sources: {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "power/cpu_frequency"
      ftrace_events: "power/cpu_frequency_limits"
      ftrace_events: "power/cpu_idle"
      ftrace_events: "power/suspend_resume"
      ftrace_events: "sched/sched_switch"
      ftrace_events: "sched/sched_waking"
      ftrace_events: "sched/sched_wakeup_new"
    }
  }
}

data_sources: {
  config {
    name: "linux.sys_stats"
    sys_stats_config {
      cpufreq_period_ms: 500
    }
  }
}

data_sources: {
  config {
    name: "linux.system_info"
  }
}

linux.ftrace 通过内核 tracepoint(预先定义的静态事件点)记录 power/cpu_frequency 的切换时刻,linux.sys_stats 补充轮询快照,linux.system_info 在采集开始时读取一次可用频点。三条路径都可能因驱动、内核配置、权限或 sysfs 导出差异而缺失,采集后应先检查数据完整性。Android 17 Perfetto CPU frequency 文档

事件驱动路径只在频率发生变化时写记录。某个 CPU 在 trace 开头保持原频点,轨道左侧就会留白;这段空白不能解释为 CPU 停止运行。轮询能补快照,却会漏掉采样点之间的切换。面向启动和掉帧的分析要保留事件驱动采集。

power/cpu_frequency_limits 值得与频率事件一起抓。Android 17 内核在 policy 限制更新时记录 min_freqmax_freq 和 policy 的代表 CPU,它能区分“governor 没有请求更高频率”和“当前上限不允许再升频”这两类线索。限制来源仍需结合 thermal、Power HAL、节电模式和厂商代码确认。Android 17 CPU frequency tracepoint Android 17 cpufreq core

Running、Runnable、idle 与频率

线程状态和频率轨回答不同问题:

观察组合当前证据支持的判断应补的证据
Running 长,报告频率高线程获得 CPU,执行时间较长调用栈、函数采样、缓存与内存停顿、PMU
Running 长,报告频率低低频、限频或低容量 CPU 值得检查policy limit、thermal、uclamp、CPU capacity、硬件频率反馈
Runnable 长线程可运行却在等待 CPU同 CPU 的运行者、优先级、调度类、CPU 压力
Sleeping 长线程主动等待锁、条件变量、Binder 进程间通信、futex(用户态同步所用的内核等待机制)、定时器
D 状态长不可中断等待I/O、驱动、内核阻塞原因
CPU idle,频率轨保留高值频率值可能是进入 idle 前的残留cpuidle、唤醒源、设备功耗

Running 时间也不能直接换算成“CPU 算力已用满”。相同 wall time(墙上时钟经过的时间)下,指令级并行度、缓存未命中、内存带宽、分支预测、SMT(Simultaneous Multithreading,同时多线程)/共享资源和迁核都会改变有效吞吐。频率分析适合定位相关性,根因判断还要依赖调用栈、硬件计数器和对照实验。

分析 App 卡顿时,可以沿着下面的顺序读取:

  1. 在 UI 线程或 RenderThread 找到对应帧和慢 slice(Perfetto 中有起止时间的事件区间)。
  2. 统计 slice 内 Running、Runnable、Sleeping 与 D 状态。
  3. 只对 Running 子区间关联 ucpu、逻辑 CPU、capacity、频率和 idle 状态。
  4. 向前覆盖足够的负载建立阶段,观察唤醒、迁核、policy 限制与升频次序。观察窗应由 workload 周期决定,不套固定毫秒数。
  5. 用调用栈、thermal(温控状态)、uclamp(utilization clamp,调度器利用率约束)、Power HAL(电源硬件抽象层)或 PMU 证据验证假设。

Perfetto 的 cpu_idle_counters(CPUIdle 状态区间表)已把原始值 4294967295 转成 -1,代表 CPU 回到 active;非负值越大通常表示更深的 idle state。具体 state 含义由 SoC 和 CPUIdle driver 定义,不能跨设备比较数字本身。

CPUIdle 的 target residency 是进入某个状态后至少停留多久才比浅层状态更节能,其中包含进入成本;exit latency 是从唤醒到开始执行第一条指令的最坏时间。短任务的首段延迟可能同时受 idle 退出与升频影响,需要观察任务唤醒前后的完整区间。Android 17 CPUIdle 文档

policy、cluster 与 CPU 标识

CPUFreq policy 是调频控制域,cluster 是处理器拓扑或容量分组。很多 Android SoC 的 policy 与 cluster 一一对应,所以多个 CPU 会同步变频;这是一种常见实现,不能当成定义。异构平台也可能让一个 cluster 出现多个 policy,或让多个逻辑 CPU 共享同一 policy。

本地设备上,policy*/related_cpuspolicy*/affected_cpus 是判断共享控制接口的直接证据。下面的命令读取 policy 成员、频率限制、governor 和可用频点。

adb shell 'for p in /sys/devices/system/cpu/cpufreq/policy*; do
  echo "[$p]"
  for f in related_cpus affected_cpus scaling_min_freq scaling_max_freq \
           scaling_governor scaling_available_frequencies; do
    if [ -r "$p/$f" ]; then
      printf "%s: " "$f"
      cat "$p/$f"
    fi
  done
done'

输出中的 related_cpus 包含 policy 关联的在线和离线 CPU,affected_cpus 表示当前在线且受该 policy 影响的 CPU。部分厂商设备不会导出所有文件,缺字段时应记录设备边界,避免补写推测值。

Trace Processor 里有三种容易混淆的标识:

  • cpu 是设备内的逻辑 CPU 编号;
  • ucpu 是 Trace Processor 的可连接 CPU 标识,多机或虚拟化 trace 也能保持唯一;
  • cluster_idcapacity 来自已采集的处理器信息,用于描述拓扑与相对容量。

只要查询涉及 sched 和 CPU counter 的时间交集,就应以 ucpu 分区。把 sched.ucpucpu_counter_track.cpu 直接比较,会混用两个标识域,在多机 trace 中尤其危险。

下面的查询读取 Android 17 linux.system_info 产生的可用频点,并与 CPU 拓扑关联。

SELECT
  c.cpu,
  c.cluster_id,
  c.capacity,
  cf.freq
FROM cpu_freq AS cf
JOIN cpu AS c ON cf.ucpu = c.ucpu
ORDER BY c.cpu, cf.freq;

Android 17 的原始公开表名是 cpu_freq,连接键是两侧的 ucpu。当前 Perfetto 的内建公开 schema 另提供 cpu_available_frequencies 视图;它不应直接替换这段面向 Android 17 配套 schema 的原始表查询。频点集合、cluster_idcapacity 和同步变频可以互相校验,policy 成员仍以设备 sysfs 或内核源码为准。Android 17 cpu_freq 表定义

三类 workload 的判断方法

启动短突发

冷启动由类加载、资源解析、Binder 往返、View 构建、编译与首次绘制等阶段组成。关键线程在低频 Running,且频率在关键 slice 结束附近才抬升,可以提出“调频响应晚于该段工作”的假设。还要排除下面几种情况:

  • 当前 CPU 属于低容量 policy,线程随后发生迁核;
  • policy 的 scaling_max_freq 已被 thermal、节电模式或系统策略限制;
  • power/cpu_frequency 表示请求值,硬件有效频率另有偏差;
  • 代码受缓存或内存停顿限制,提高频率对 wall time 的收益有限;
  • UI 线程 Running 之外仍有较长 Runnable、Binder 或 I/O 等待。

优化应先减少工作量:删减首帧前任务,合并可延后的工作,降低同步 I/O 与跨进程往返。系统侧调优还要结合 PerformanceHint / ADPF(Android Dynamic Performance Framework,应用向系统提交性能提示的框架)、uclamp、Power HAL 和目标设备的能效曲线验证,不能从一条低频轨直接推出“锁最高频”。

连续渲染负载

滑动、动画和游戏帧循环具有重复节奏。分析时按帧切窗,并分别观察 UI 线程、RenderThread(应用渲染线程)、GPU 提交线程与 SurfaceFlinger(系统合成服务)。频率升降若总是落后于负载相位,可能带来周期性 wall time 波动;线程迁往不同 capacity CPU、Runnable 排队或 GPU fence(生产者与消费者之间的同步对象)等待也会产生相似现象。

频率高不保证帧能按期完成,频率低也不必然是异常。帧内工作量较少时,低频可能正是能效目标。判断依据应是相同设备、相同温度和相同 workload 下的帧时序与频率变化,并结合 2.x 渲染章节的应用、RenderThread、GPU、合成与显示链路。

后台批处理

压缩、数据库迁移、索引和日志整理通常对单帧时序不敏感。低频可能延长 active 时间(CPU 非空闲时间),高频可能缩短完成时间并提高瞬时功耗。哪种方案能耗更低,取决于电压频率曲线、内存行为、policy 能效、idle state、唤醒次数和 thermal 状态。

频率轨不含电压,也不等价于整机功耗。后台任务的评估至少要同时比较完成时间、CPU active/idle 分布、唤醒次数、温度和硬件功耗数据。高频更快或低频更省电都只能作为待验证假设。

用 SQL 重建频率时间线

Android 17 把原始频率和 idle 事件建模为 counter,轨道属性放在 cpu_counter_track。下面的查询用于确认 trace 是否含有原始数据。

SELECT
  c.ts,
  t.name,
  t.cpu,
  c.value
FROM counter AS c
JOIN cpu_counter_track AS t ON t.id = c.track_id
WHERE t.name IN ('cpufreq', 'cpuidle')
ORDER BY c.ts, t.cpu
LIMIT 200;

cpufreq 的值单位是 kHz。cpuidle 原始值 4294967295 代表退出 idle。结果为空时要回查采集配置和设备支持情况;UI 没有显示轨道时也应执行该查询,因为 Android 17 文档记录过“缺少 idle 事件时 UI 不渲染频率轨,但 SQL 数据存在”的显示问题。

Perfetto 标准库已经把 counter 转成带 dur 的区间。下面的查询读取频率区间和规范化后的 idle 区间。

INCLUDE PERFETTO MODULE linux.cpu.frequency;
INCLUDE PERFETTO MODULE linux.cpu.idle;

SELECT ts, dur, ucpu, cpu, freq
FROM cpu_frequency_counters
ORDER BY ts, cpu
LIMIT 100;

SELECT ts, dur, cpu, idle
FROM cpu_idle_counters
ORDER BY ts, cpu
LIMIT 100;

cpu_frequency_counters 同时提供 ucpu 与逻辑 cpucpu_idle_counters.idle = -1 表示 active。dur = -1 可能表示区间延续到 trace 末尾,做交集或聚合时要按查询目标处理开放区间。Android 17 frequency 标准库 Android 17 idle 标准库

下面的模板用 ucpu 对调度区间与频率区间做 SPAN_JOIN(按时间求区间交集)。它是后续频率分布、迁核和帧窗口查询共用的准备步骤。

INCLUDE PERFETTO MODULE linux.cpu.frequency;

CREATE PERFETTO VIEW _running_spans AS
SELECT ts, dur, ucpu, cpu, utid
FROM sched
WHERE dur > 0;

CREATE PERFETTO VIEW _frequency_spans AS
SELECT ts, dur, ucpu, freq
FROM cpu_frequency_counters
WHERE dur > 0 AND freq IS NOT NULL;

CREATE VIRTUAL TABLE _running_with_frequency
USING SPAN_JOIN(
  _running_spans PARTITIONED ucpu,
  _frequency_spans PARTITIONED ucpu
);

结果中的每一行都是“某线程在某逻辑 CPU 上 Running,且该 CPU 处于某个已知频率区间”的时间交集。使用内连接会舍弃未知频率区间,汇总时应同时报告被覆盖的 Running 时间,防止把缺失数据当成零。

下面的查询以 com.android.settings 主线程为可执行示例,统计它在各频点上的 Running 时间。

WITH target_thread AS (
  SELECT t.utid
  FROM thread AS t
  JOIN process AS p ON t.upid = p.upid
  WHERE p.name = 'com.android.settings'
    AND t.tid = p.pid
),
running_freq AS (
  SELECT swf.dur, swf.freq
  FROM _running_with_frequency AS swf
  JOIN target_thread USING (utid)
  WHERE swf.dur > 0
)
SELECT
  freq,
  SUM(dur) / 1000000.0 AS running_ms,
  ROUND(100.0 * SUM(dur) / SUM(SUM(dur)) OVER (), 2) AS pct
FROM running_freq
GROUP BY freq
ORDER BY running_ms DESC;

把包名替换为目标进程名即可复用。tid = pid 用于定位 Linux 主线程,随后全程使用 utid,从而避开长 trace 中 tid 复用。这里的百分比是“已知频率覆盖范围内的 Running 时间占比”,不能解释为指令占比、周期占比或能耗占比。

下面的查询把 UI 线程 Running 区间裁到 android_frames(FrameTimeline 标准库生成的帧表)的帧窗口内,适合检查掉帧附近的频率分布。

INCLUDE PERFETTO MODULE android.frames.timeline;

SELECT
  f.frame_id,
  swf.cpu,
  swf.freq,
  SUM(
    MIN(swf.ts + swf.dur, f.ts + f.dur) -
    MAX(swf.ts, f.ts)
  ) / 1000000.0 AS ui_running_ms
FROM android_frames AS f
JOIN _running_with_frequency AS swf
  ON swf.utid = f.ui_thread_utid
 AND swf.ts < f.ts + f.dur
 AND f.ts < swf.ts + swf.dur
WHERE f.process_name = 'com.android.settings'
GROUP BY f.frame_id, swf.cpu, swf.freq
ORDER BY f.frame_id, ui_running_ms DESC;

这个结果只覆盖 UI 线程。RenderThread 需要改用 f.render_thread_utid 单独查询;GPU、SurfaceFlinger 和显示侧仍要沿渲染链路核对。帧窗口内缺少频率行时,先计算该帧的 Running 时间与频率覆盖率,再讨论低频相关性。

线程迁核可以直接从公共结果里观察。下面的查询列出目标主线程连续 Running 区间之间的 CPU、cluster 与频率变化。

WITH target_thread AS (
  SELECT t.utid
  FROM thread AS t
  JOIN process AS p ON t.upid = p.upid
  WHERE p.name = 'com.android.settings'
    AND t.tid = p.pid
),
ordered AS (
  SELECT
    swf.ts,
    swf.dur,
    swf.cpu,
    c.cluster_id,
    c.capacity,
    swf.freq,
    LAG(swf.cpu) OVER (ORDER BY swf.ts) AS prev_cpu,
    LAG(c.cluster_id) OVER (ORDER BY swf.ts) AS prev_cluster_id
  FROM _running_with_frequency AS swf
  JOIN target_thread USING (utid)
  JOIN cpu AS c ON c.ucpu = swf.ucpu
)
SELECT *
FROM ordered
WHERE prev_cpu IS NOT NULL
  AND (cpu != prev_cpu OR cluster_id != prev_cluster_id)
ORDER BY ts;

该查询展示相邻的已知频率 Running 区间,不保证两行之间没有 Runnable、Sleeping 或未知频率空洞。迁核原因需要结合 thread_state、wakeup(线程唤醒事件)、uclamp、调度优先级和系统负载判断。

端侧 AI 推理中的 governor 协同

论文 Dissecting the Impact of Mobile DVFS Governors on LLM Inference Performance and Energy Efficiency 研究了 CPU、GPU 和内存调频各自决策时的能效错配。实验设备是 Pixel 7 与 Pixel 7 Pro,SoC 为 Tensor G2,系统为 Android 13;测试采用 llama.cpp、OpenCL/CLBlast、TinyLlama 1.1B、StableLM-Zephyr 3B 和 Llama-2 7B,并在 root、battery bypass(拆机旁路电池并由功耗仪供电)、屏幕关闭和外部功耗采样条件下运行。

在这组实验边界内,独立 governor 相对同能耗的频率组合,部分 prefill(处理输入提示)/ decode(逐 token 生成)延迟最多增加 40.4%。论文提出统一管理 CPU、GPU 和内存频率的 energy-aware governor FUSE;在相同单 token 能耗下,它平均降低 7.0%—16.9% 的 TTFT(Time to First Token,首 token 延迟)和 25.4%—36.8% 的 TPOT(Time per Output Token,平均每个输出 token 的时间)。论文页面

这些数字不能外推到 Android 17、其他 SoC、NPU(Neural Processing Unit,神经网络加速器)后端或不同推理运行时。它们提供的是一种诊断视角:GPU 执行仍依赖 CPU 提交,KV cache(注意力计算复用的 key/value 缓存)与权重访问受内存系统影响,单看 CPU 利用率可能低估整条推理流水线的时序需求。

端侧推理 trace 至少要把 CPU sched/cpufreq、GPU job 与频率、内存带宽或 DDR(Double Data Rate,双倍数据率内存)频率、thermal、TTFT、TPOT 和单 token 能耗放在相同实验中。设备不暴露 GPU 或 DDR 数据时,结论应明确限定为 CPU 侧观察。

与 EAS、uclamp、thermal 交叉验证

CPU Frequency 是结果信号,解释其来源还要结合下面几组证据:

  • EAS(Energy Aware Scheduling,能耗感知调度)与 task placement(任务放置):确认线程所在 CPU 的 capacity、迁核方向和同核竞争,详见 5.1 节;
  • uclamp 与 PerformanceHint:确认任务的利用率下限、上限及会话提示是否改变 schedutil 输入,详见 5.2 节;
  • CPUFreq policy:对照 scaling_min_freqscaling_max_freq、governor、cpu_frequency_limits 和可用频点;
  • thermal 与电源策略:检查温度、冷却设备、节电模式、Power HAL 和 sustained workload,详见 11.1 节;
  • 执行效率:用 Simpleperf / perf 采样工具、PMU、AMU 或厂商计数器排除缓存、内存和 CPU 前端停顿。

Android 17 通用内核能说明 CPUFreq、schedutil、uclamp、EAS 与 CPUIdle 的框架。设备上的 energy model(调度器估算性能与能耗的数据表)、频点表、thermal policy、Power HAL 和 vendor hook 可能来自厂商内核或闭源组件。没有目标设备的源码、sysfs 和 trace,结论只能停留在通用框架层。

线上与工具预设的采集边界

本地 adb perfetto 接受完整 TraceConfig,在设备允许的范围内可以显式启用分析所需的数据源。Android Studio Profiler 使用工具自己的采集配置,版本与入口不同会得到不同字段;分析前应检查 cpu_counter_track 和 trace metadata,不应按界面名称推断采集内容。

Android 17 的 ProfilingManager(面向应用的系统性能采样 API)system trace 由平台 Configs 生成固定配置。该配置包含 compact sched(压缩编码的调度事件)、进程信息、应用 atrace 分类和 SurfaceFlinger FrameTimeline,未启用 power/cpu_frequencypower/cpu_idlelinux.sys_statslinux.system_info。system trace 结果还会经过 redaction(敏感信息裁剪),并受系统 rate limiter(采样频率限制)、参数范围和设备配置约束。它可以支持应用侧线上性能采样,却不能替代本地 CPU DVFS TraceConfig。Android 17 Profiling 配置 Android 17 ProfilingService

拿到线上 trace 后,先列出实际存在的数据源和轨道。缺少 idle、policy limit、GPU 或 thermal 时,报告应写成“已观测 CPU 频率与调度状态的相关性”,不能升级为整机能耗或 governor 根因。

误判清单

  • USB 改变 idle 条件:Android 17 Perfetto 文档指出,许多 Android 设备接入 USB 时,USB driver stack 持有 wakelock(阻止系统进入休眠的锁),部分 idle state 不会出现。USB trace 的 idle 分布未必代表脱线使用。
  • 轨道开头空白power/cpu_frequency 只记录切换,空白可能来自初始值缺失;用轮询补快照,并在 SQL 中报告覆盖率。
  • idle 区间的频率残留:CPU clock-gated(关闭时钟以降低动态功耗)后,轨道可能保留进入 idle 前的频率。频率值不能替代 idle state 或功耗数据。
  • 请求频率等同硬件频率cpu_frequencyscaling_cur_freq 多数情况下更接近软件请求或驱动报告值;需要硬件反馈才能讨论有效频率。
  • 高频等同高吞吐:缓存未命中、内存带宽、分支和共享资源会让高频 Running 仍然很慢。
  • 低频等同 governor 反应慢:policy 上限、thermal、uclamp、节电模式、迁核和 vendor hook 都可能限制结果。
  • 同步变频等同 cluster:同步轨迹只能提示共享 policy;用 cluster_id、capacity、related_cpus 和源码确认拓扑。
  • 频率等同功耗:轨道没有电压、漏电、内存、GPU 和屏幕功耗。整机能耗需要硬件功耗计或可靠的设备级计量。
  • UI 没轨等同没数据:Android 17 文档记录过 cpuidle 缺失导致 UI 不渲染 cpufreq 的问题,SQL 查询才是数据存在性的判据。
  • 厂商策略可以互推:高通、联发科、三星、Google Tensor 的 policy、Power HAL、thermal 与 GPU/内存调频实现不同,结论必须绑定设备、版本和 workload。

查询结果应怎样写进报告

一份可复核的 DVFS 结论至少包含:

  1. 设备、构建版本、内核版本、Trace Processor 版本与采集配置;
  2. workload、温度、充电/USB、屏幕、节电模式和重复次数;
  3. 目标线程的 wall time、Running、Runnable、阻塞与频率覆盖率;
  4. CPU 的逻辑编号、ucpu、capacity、cluster 与 cpufreq policy;
  5. 频率切换、policy limit、idle、迁核和 thermal 的时间关系;
  6. 代码或调用栈热点,以及必要的 PMU、AMU、GPU、内存与功耗证据;
  7. 明确区分观测、推断和已验证根因。

“目标线程在已知频率覆盖的 Running 时间中,有多少比例落在某频点”是可复核观测。“governor 导致掉帧”属于因果结论,需要对照实验:固定 workload,控制温度与系统状态,改变一个调频相关变量,并确认帧时序随之稳定变化。这样写,频率轨才会从截图线索变成可验证的性能证据。

小结

CPU 频率轨是调频请求或驱动报告的状态信号,不是指令执行量、瞬时硬件有效频率或功耗。分析时要先把频率点还原为区间,再与目标线程的 Running 区间相交,并报告频率覆盖率。因果判断还必须联合 CPU 拓扑、policy limit、idle、迁核、uclamp、thermal 和对照实验;缺少 GPU、DDR 或功耗数据时,结论应明确限定在 CPU 侧观测。