外观
Frame Time 实时采集与流畅度报告 v4
指标八要素
| 要素 | 内容 |
|---|---|
| 定义 | 相邻两个被 exact debugid 接受的 presented-frame 时间戳之差 |
| metricID | ios.frame.time;Session 汇总指标沿用 ios.frame.* Registry |
| scope | foreground-device;不表示目标 PID 独占的渲染帧 |
| unit | milliseconds |
| 来源字段 | CoreProfile RAW v2 kd_buf.timestamp、Mach numer/denom、exact debugid 0x31800318 |
| 质量语义 | 首帧 warmup;可解码有限值 candidate;后台 not-foreground;头/时基/debugid 失配则 missing 或 unsupported-schema,绝不以 1000/FPS 伪造 |
| 当前校准状态 | iOS 16/17/18 CoreProfile 来源 E4 candidate;本版增加选中指标后的实时投影,等待新构建的真机 UI 验收 |
| 版本历史 | decoder core-profile-v2-presented-frame.candidate.v2;实时投影 frame-time-realtime.candidate.v1;报告 frame-report.candidate.v2 |
实时公式和窗口
text
timestampNs[i] = floor(machTicks[i] × numer / denom)
frameTimeMs[i] = (timestampNs[i] - timestampNs[i-1]) / 1,000,000
instantFPS[i] = 1,000 / frameTimeMs[i]- 底层通信服务:
com.apple.instruments.server.services.coreprofilesessiontap - 底层实现归属:基于
pymobiledevice3.services.dvt.instruments.core_profile_session_tap.CoreProfileSessionTap打开 RAW v2 协议流(配置bm=0/1,按选择启用sampling-trigger)。Perfowl 自研RealtimeFrameDecoder解码 64 字节kd_buf结构。 - CoreProfile 仅在用户选中
Frame Time时启动,与其他指标共用唯一 DTX owner。 - decoder 持有当前 Session 的 RAW v2 追加缓冲,每次只发布新增 accepted timestamp;重复解码结果不重复发布。
- 首个 accepted timestamp 只建立前值,不生成 0 ms。逆序、相等、越 generation 的 timestamp 不参与差分。
- 目标 App 进入后台时实时值立即失效(
not-foreground);回到前台后必须等待新的 accepted presented-frame 事实。 - 计算层保留 Double,UI 可格式化一位小数,Session/CSV 不回写 UI 取整值。
汇总报告与流畅度详细算法(frame-report.candidate.v2)
所有汇总指标均直接由 Session 内接受的完整 FrameTime 序列计算得出:
1. 动态平滑基线与三档 Jank 判定
- 动态平滑基线 ($B_n$):前 3 帧平均耗时 $$B_n = \frac{FT_{n-1} + FT_{n-2} + FT_{n-3}}{3} \quad (\text{当样本数 } \ge 3)$$
- 动态门槛 ($D_n$):$D_n = 2 \times B_n$
- 三档 Jank 判定规则:
- SmallJank:$FT_n > D_n \quad \text{且} \quad FT_n > 41.66\text{ ms}$
- Jank:$FT_n > D_n \quad \text{且} \quad FT_n > 83.33\text{ ms}$
- BigJank:$FT_n > D_n \quad \text{且} \quad FT_n > 125.00\text{ ms}$
- 10 分钟频次折算: $$\text{Jank/10min} = \frac{\text{Count}}{\text{Duration (seconds)}} \times 600$$
2. 卡顿率(Stutter %)
所有被判定为 Jank 的帧中,其超出基线的卡顿耗时之和占测试总时长的比例: $$\text{Stutter%} = \frac{\sum_{i \in \text{Jank Frames}} \max(0, FT_i - B_i)}{\text{Session Total Duration (ms)}} \times 100%$$
3. 平均 FPS 与 1% Low FPS
- Average FPS:采用加权真实均值(即 1000 除以平均帧耗时),而非简单帧率算术平均: $$\text{Avg FPS} = \frac{1000}{\text{mean}(FT)}$$
- 1% Low FPS:将所有帧耗时降序排列,取耗时最长的最差 $1%$ 帧($\lceil 0.01 \times N \rceil$ 个),计算其平均帧耗时的倒数: $$\text{1% Low FPS} = \frac{1000}{\text{mean}(\text{Slowest 1% } FT)}$$
4. 掉帧率(Drop Rate)与稳态区间
- Dropped Frames / Hour:统计相邻帧瞬时 FPS 跌幅大于 8 的总次数($\text{DropCount} = \sum [FPS_i - FPS_{i+1} > 8]$),折算每小时频次: $$\text{Drop / Hour} = \frac{\text{DropCount}}{\text{Duration (seconds)}} \times 3600$$
- FT >= 100ms 占比:帧耗时超过 100ms 的帧数占总帧数的百分比。
- MedRange% (80%~120% 稳态占比):落在 $[0.8 \times \text{Median(FPS)}, 1.2 \times \text{Median(FPS)}]$ 区间内的帧数占比。
- TinyJank 与 Smooth:因竞品未公开可信严谨算法,Perfowl 保持
Unsupported并输出null,绝不自行编造。
版本历史
- v4(2026-09-06):增加按选择启用 CoreProfile、追加解码、实时 Frame Time 和前后台失效规则;来源:P4 工作台指标支持收口方案。
- v3(2026-09-05):RAW v2 重放与 iOS 16/17/18 来源校准,已被本页取代。