Skip to content

静止画面口径、统一每秒 FPS 与 Excel 逐秒/逐帧明细 ​

Owner 决策(2026-11-02) ​

  1. 静止页面与 PerfDog 一致:iOS 26 帧链首尾相接时,静止秒的 FPS 记 0,静止后首帧按真实间隔发布帧耗时;帧链断开时仍留空。
  2. FPS 统一为“逐帧事件每秒计数”,工作台、原生报告、Excel 共用;无逐帧数据时用 Graphics FPS 兜底并标注;平均 FPS 旁附“静止秒数占比”。
  3. Excel 在“概览”“详细数据”之后增加“逐秒数据”“逐帧数据”两页。

依据:PerfDog 帧统计口径对照、报告审视。两份 PerfDog 导出中,每秒 FPS 之和等于总帧数,静止秒为 0,长间隔帧按真实值记录。

公式 ​

帧耗时(FrameUpdate) ​

  • 本帧 FramePrevious 精确等于上一条已解码帧的呈现时刻时,帧耗时 = 两者之差,不再设 200 ms 长间隔门(frameupdate-presentation-gap.v2)。帧链相接本身证明中间没有其他呈现。
  • 断链(FramePrevious 不等于上一帧呈现时刻)仍为 discontinuity、置空;首帧与来源/代际切换仍为 warmup。
  • iOS 17 FrameLifetime 保持原规则(12 个刷新间隔门)。

实时每秒 FPS ​

  • 设备时间水位 W:ActivityTrace 流中最近收到的任一条消息(含被白名单拒绝的消息)的设备 Mach 时间,换算为 ns。流按设备时间顺序到达,因此 W 之前不会再有新帧。
  • 当 W − 最后一帧呈现时刻 ≥ 1 s:FPS = (W − 1 s, W] 内的呈现帧数,可为 0,质量 valid(activitytrace-window-fps.watermark.v2)。
  • 否则沿用原逐帧窗口计数;ActivityTrace 流整体沉默(水位不前进)超过 1 s 时仍为 stale-source,不补 0。

报告每秒 FPS 序列 ​

  • 以会话第一帧呈现时刻为 0 点,按 1 秒分桶统计呈现帧数,F[k] 为第 k 秒的帧数。
  • 只采用完整且被证实的秒:桶完全落在“首帧 … 最后一帧”之间,且不跨越断链区间;断链覆盖的秒不计入(不当 0)。
  • FPS 类统计(Avg/Median/Std/Var/≥18/≥25/MedRange/Drop)一律基于 F;Drop = #{F[k−1]−F[k]>8} ÷ (n−1) s × 3600。
  • 静止秒数占比 = #{F[k]=0} ÷ n × 100。
  • 无逐帧数据(来源未选中)时退回产品 FPS 采样,公式版本标 graphics-fallback。

Excel 新增页 ​

  • 逐秒数据:每秒一行——时间(s)、FPS、帧数中 Jank/BigJank 次数、最大帧耗时、应用 CPU、整机 CPU、应用内存、应用上行/下行 KB/s、GPU、电池温度、标注、截图编号(若有)。
  • 逐帧数据:每帧一行——序号、呈现时间(s)、帧耗时(ms)、是否 Jank/BigJank。

验收 ​

  • 单元:静止长间隔帧发布真实值;水位推进后的 0 FPS;断链秒不进入每秒序列;两份 PerfDog 导出的 FPS 类与每秒帧数复算一致。
  • 真机 E4:新包会话中静止段 FPS 为 0、曲线不断;报告只有一个平均 FPS 且与逐秒页均值一致。

Implementation Update · 2026-11-02 ​

文件修改
display_performance.pyFrameUpdate 相接长间隔照常发布(v2,去掉 200 ms 门);ActivityTrace 过滤流记录设备时间水位(任意消息的 time,只读时间不留内容),适配器提供 device_watermark();投影器暴露最后呈现时刻与帧时基;8 字节数值后多带空字节时按零填充接受。
p95_frame_window.pyFrameUpdate 帧去掉 200 ms 上限,与解码器一致。
realtime.py实时快照:FrameUpdate 主来源健康、水位在 1.5 s 内刷新且越过最后一帧 ≥ 1 s 时,FPS 记 0(watermark-idle.v2);启动 ActivityTrace 时把适配器水位接入投影器。
frame_statistics_policy.py新增 PerSecondFrameCounter(首帧为 0 点整秒计数、断链秒排除、末秒不计、可断点恢复)与 PerfDog 形 FPS 统计映射。
frame_derivation.py、online_report.py、report_engine.py离线/在线帧汇总增加每秒帧数统计与静止占比;统计报告 FPS 类优先使用每秒帧数,无帧流时退回 FPS 采样。断点状态新增 perSecond,旧断点按 offline-required 拒绝。
Swift ReportDerivedStatistics / SessionReportView / ReportWorkbookPresentation原生报告概览 FPS 卡片显示与帧统计同一平均 FPS 并附“静止 x%”;Excel 概览用“平均FPS(每秒)+ 静止秒数占比”,有每秒统计时不再重复输出 FPS 采样分布组;概览分组合并。
Swift ReportWorkbookDetailSheets(新)/ PerfDogLayoutWorkbookWriterExcel 新增“逐秒数据”(时间、FPS、帧耗时、应用/整机 CPU、内存、上下行、GPU、电池温度、标注、截图编号)与“逐帧数据”(序号、时间、帧耗时、去重 Jank/BigJank);逐帧页流式写出,上限 1,048,575 行。
Swift SessionReport读取逐帧记录时跳过无时间戳的解码拒绝行(此前一行即令整份逐帧记录丢弃,导致报告逐帧为空)。

偏差 ​

  • 原计划用 Graphics FPS = 0 佐证静止。实测 Graphics 在 App 静止时常为 1/3/5(整机口径),且上限 60(120 Hz 设备),不可靠,改为 ActivityTrace 设备时间水位。
  • 逐秒页暂未包含“该秒 Jank/BigJank 次数、最大帧耗时”:逐帧记录是设备时钟,与会话秒的对齐锚点尚未落档,先在逐帧页按帧标注 Jank/BigJank,避免错位。
  • Excel 数值本已按 0.00 格式显示、时长为“秒”格式;审视中“未格式化”一条系读取原始值所致,撤回。

验证(E2) ​

  • Python:FrameUpdate 10 项(含静止长间隔、水位 0 FPS 与无证据不补 0、空字节值)、PerfDog 对照 4 项(以 PerfDog 每秒帧数为输入时 FPS 类 8 项完全一致;真实帧序列平均/中位 FPS 分别在 1.5/1.0 以内)。
  • Swift:报告/导出/工作台/基础 82 项通过;新增逐秒/逐帧页、无时间戳行跳过断言。
  • 真实会话(240,约 86 秒)导出:逐秒 80 行、逐帧 3,056 行(Jank 28 / BigJank 15 标注),概览含总流量与静止占比。
  • 全量回归:Swift 442 项(排除既有挂起的 UserReportIntegrationTests),唯一失败为概览分组合并后的旧组名断言,已改为检查概览“画面表现”与详细数据“帧统计”后通过;Python 3,144 项仅 16 项改动前已存在的原生扩展/sw1 环境失败。

Validation Update · 2026-11-02:内部包 241 构建与同址安装(E3) ​

  • 来源:main 3fc4176a(含本批与同日报告导出/CPU/流量修正),Developer ID 签名构建 249.8 秒,来源门禁 passed。
  • 校验:冻结核心 107 个模块与 CLI 和源码一致,严格签名、DMG 与安装后文件树一致,安装后 probe ready;启动器只剩 /Applications/Perfowl.app。
  • 产物:MacApp/dist/Perfowl-241-macos-x86_64-internal.dmg,145,169,646 B,SHA-256 18072b11899808e84679b5d196a5b06c9d2c9dd7a3fd29d863946fa93d3c31ac;240 安装包与回滚包在私有归档,回滚已核对。
  • 未完成:公证暂缓(Gatekeeper exit 3);真机 E4 待 Owner 录含滑动与静止的会话。

Validation Update · 2026-11-02:241 真机复测与帧耗时调整(E4 / E2) ​

Owner 在 241 上录约 52 秒会话(滑动与静止交替)并导出 Excel(取消勾选截图):

  • FPS:47 个采样中 22 秒为 0,曲线不断(E4)。
  • 导出:只生成 Excel,无截图 ZIP;逐秒 47 行、逐帧 1,084 行(E4)。
  • 解析:1,084 个帧事件 0 个 unsupported-schema,空字节值修正确认(E4)。
  • 静止长帧:逐帧数据中 12 个 1.8–2.5 秒的相接长帧按真实值发布(E4)。
  • 问题 1 · 帧耗时曲线仍断:没有新帧的秒不应有帧耗时值;且每秒只取采样时刻的最新一帧,长帧被同秒后续短帧覆盖(每秒最大只显示 634 ms)。Owner 决定:曲线跨“暂无新帧”连线、每秒取该秒最大帧耗时。已实现(E2):投影器记录上次发布以来最大有效帧耗时并在发布时清零;DisplayMetricPresentation.bridgesFrameTimeGap 仅对 missing-no-new-frame / stale-device-source 连线,工作台与报告曲线共用。
  • 问题 2 · 静止占比两处不一致:报告 26.7%、Excel 46.8%。原因是整秒切分起点不同(该 App 静止时每 2.5 秒有约 0.27 秒、32 帧的小动画,常跨两个桶)。已改为 Swift 读取封存的 perSecondStaticPercent,与 Avg(FPS) 同一计数(E2)。
  • 回归:Swift 全量 443 项 0 失败(排除既有挂起的 UserReportIntegrationTests);Python 3,145 项仅 16 项改动前已存在的原生扩展/sw1 环境失败。

Perfowl · Performance Observer