外观
自有测试 App:全指标覆盖与长期测试工具设计
供 Owner 审核,未批准实施。 这是设计与口径盘点,不是 App 功能或指标验收通过。2026-10-15 Owner 决定:完整覆盖设计、按当前指标逐步实现;数值允许合理误差、重点排除明显偏离;本轮范围包含启动;工具还需包含网络、卡顿、崩溃、WebView 等真实业务测试。当前不改产品/Fixture、不构建覆盖手机、不采新会话、不发布网站。
1. 审核摘要与范围
- 真实 Registry 66项 = 54项非启动 + 12项启动。按当前
PerformanceModels.swift逐项提取,与site/data/metric-contracts.json逐字段核同,不沿用旧规则中64的文字快照。 - 本稿附66行一对一覆盖矩阵,保留既定非启动54项的相对顺序;启动新增独立区块,不把旧台账自动改成“7/66已验收”。旧7/54是历史登记,旧原件已删除不等于本轮复核通过。
- 54/66是指标数量;网络异常、崩溃、WebView、组合业务是场景能力,不伪造新metricID或掩盖尚未覆盖项。
- 不承诺每项都可“设定精确系统读数”。每项明确:负载控制、独立/半独立参照、产品来源、系统限制。设计覆盖≠功能实现≠数值校准≠正式验收。
- 审核配套位于仓库
internal/design/fixture-lab-v1/:离线review.html(可切换场景、查看66项并导出审核意见)、metric-coverage.json(机器可读矩阵)。原型不连接设备,所有参数仅设计示意,没有假实测结果。机器路径是仓库内文件,不作为站点链接。
2. 现有 App 盘点:复用而不是另造一套
当前 PerformanceProbeFixtureApp.swift 已有CPU 1/2线程与短峰、内存+10/+50MiB/释放及阶梯、主线程停顿、简单请求、受控Crash入口;还有多种默认关闭的参考模块。静态可见不代表当前手机每项已通过。主要短板是入口碎片化、参数与参考模式耦合、统一运行协议不足、网络/WebView/存储/图形场景覆盖不完整。本设计在原工程/同一安装身份下逐步模块化,不新增平行手机App或Mac客户端,不一次性删除/重写旧参考模块。
3. 产品结构与页面
暂称 Perfowl Test Lab(展示名待确认;原安装身份保持)。五个一级入口:
- 测试台:当前场景、参数摘要、环境/配额、明确开始/停止、当前阶段、实际工作量、记录质量、最近终态;一键回到轻量空闲。
- 场景库:内存、CPU/调度、网络、帧率/卡顿、存储、GPU、启动、设备/能耗、WebView、崩溃/恢复、组合业务;搜索/收藏/预置配方。
- 覆盖矩阵:66项逐行列出定义/单位/scope、对应场景、可控制量、参考方式、限制、实现状态、验收状态与证据。独立显示不支持/待核/未测,不用一个“已覆盖”绿点糊成通过。
- 运行记录:参数与实际完成量、阶段时间线、异常、停止理由、文件与摘要、与Perfowl/PerfDog对照的关联状态;单轮/配方导出。
- 设置与配方:资源上限、受控服务地址、参考采样模式、隐私导出、清理预览、能力列表。隐藏旧调试开关,专家设置显式开启且每轮落有效配置。
场景详情统一:目的 → 预置档 → 参数 → 实际工作 → 可核对指标 → 限制 → 运行记录。单位一律明确;业务字节、源生系统值与候选派生值不同颜色/标签区分,不在UI伪装同一真值。
4. 统一执行与可重复性
状态机:idle → prepared → armed → baseline → stimulus → hold → recovery → sealing → completed;用户取消、资源超限、错误均进入 stopping → cancelled/partial/failed,不得改记completed。Crash是显式终止支路,正常seal缺失是预期事实,不自动伪造成功。
- 配方固定schema/version、随机种子、参数、阶段顺序、总量上限、时限、参考模式、环境标签与有效配置摘要;开跑后不可静默改值。手动探索与正式配方运行分开。
- 每轮保存计划值/实际值/单位/工作次数/成功失败/开始结束;所有段(准备、过渡、尾部、失败)保留,正式分母只按批准合同计算,不删异常段美化。
- iPhone内用明确单调时钟,保留原ticks/timebase;墙钟仅用于查找,主机/手机/JS时钟分域,配对需保留桥接误差,不用ISO字符串直接做毫秒级跨机相减。
- 本地Run标识不在公开页暴露真实设备/进程/会话身份。Owner审核使用别名,原始身份只私有留存;导出前预览并脱敏URL/token、账号、cookie等。
- 尽量只运行当前场景所需参考。默认轻量事件记录/1Hz参考,UI ≤2Hz更新;逐帧/高密度采样显式诊断模式,不常驻全指标监控。此为设计目标,待真实测开销,不把1Hz等参数当已验证成果。
- 停止必须取消任务、关闭文件/连接、释放自有内存/图形资源;启动默认idle,不自动续跑旧负载。残留配置、运行态与崩溃恢复独立显示。
5. 场景库与可控量
MEM · 内存实验室
- 优先级:首批实现。
- 可控制:分配20/40/80 MiB;步长、触页/仅保留地址、保持/释放、可回收对象模式;有界上限。
- 配方示例:空闲20s → 触页20MiB保持20s → 40MiB保持20s → 80MiB保持20s → 释放观察40s(示例不是替换原正式轮时长/分母)。
- 对照记录:分配/触页/释放事件账本;受支持的本进程内存API与整机VM参考分开记录。
- 额外工具能力:内存阶梯、循环分配释放、对象缓存、后台前台恢复;不自动制造OOM。
- 边界:申请量不是footprint,也不是Free变化真值;虚拟预留不等于常驻;失败、压缩和系统回收均保留。
CPU · CPU与调度
- 优先级:按指标顺序完善。
- 可控制:1/2/可用核数工作线程;10/30/60/90%忙闲配比;1s周期与突发;线程创建/退出。
- 配方示例:空闲 → 单线程阶梯 → 多线程 → 短突发 → 恢复(示例不是替换原正式轮时长/分母)。
- 对照记录:实际累计CPU时间/elapsed;线程生命周期;切换/唤醒参考仅API可读时。
- 额外工具能力:锁竞争、串并行任务、任务取消、周期唤醒;不使用无限死锁。
- 边界:忙闲配比不是系统CPU百分比;不强绑大小核;定时次数不等于中断唤醒次数。
NET · 网络实验室
- 优先级:按指标顺序完善。
- 可控制:上下行/双向;1/10/50MiB;速率上限;1/2/4并发;HTTP缓存/压缩/重定向;连接复用。
- 配方示例:空闲 → 固定上传 → 空闲 → 固定下载 → 双向 → 排空(示例不是替换原正式轮时长/分母)。
- 对照记录:App业务字节+服务端收发/摘要+URLSession任务指标;各层独立。
- 额外工具能力:慢首包、慢响应、4xx/5xx、超时、截断、取消重试、Range、WebSocket心跳。
- 边界:业务/TLS/网络层不混算;速率是限速请求不是精确吞吐;模拟响应延迟不是物理丢包。
FRAME · 帧率与卡顿
- 优先级:按指标顺序实现。
- 可控制:请求15/30/60及硬件支持的刷新范围;主线程40/100/200/500ms有界阻塞;频率/次数。
- 配方示例:平滑滚动 → 单次阻塞 → 周期阻塞 → 后台计算对照 → 恢复(示例不是替换原正式轮时长/分母)。
- 对照记录:实际阻塞开始结束与工作完成;DisplayLink仅回调参考;外部呈现序列独立复算。
- 额外工具能力:长列表、图片解码、布局抖动、动画、Canvas/Metal;不以截图轮询作帧真值。
- 边界:请求帧率不是实际呈现FPS;注入1次阻塞不等于某算法恰报1次Jank。
IO · 存储与文件
- 优先级:按指标顺序实现。
- 可控制:顺序/随机;读取/写入;1/64/1024KiB块;总量;覆盖/新文件;缓存与同步标志。
- 配方示例:空闲 → 有界写入 → 明确缓存条件读取 → 同步收尾 → 清理自有工作文件(示例不是替换原正式轮时长/分母)。
- 对照记录:成功read/write字节、偏移、调用结果与时刻;系统I/O计数另列。
- 额外工具能力:小文件、JSON/图片缓存、SQLite事务、磁盘不足的预置错误路径。
- 边界:逻辑写入不等于物理磁盘写入;fsync不承诺消除全部系统缓存;不访问其他App。
GPU · GPU与图形
- 优先级:按指标顺序实现。
- 可控制:简单/复杂几何、纹理、分辨率倍率、过绘、shader负载;固定时长。
- 配方示例:静态 → 几何负载 → 像素负载 → 混合 → 恢复(示例不是替换原正式轮时长/分母)。
- 对照记录:实际提交/完成GPU工作;同口径外部GPU计数与PerfDog可用项。
- 额外工具能力:图形压力、帧率与耗能联动;资源复用避免测试台自身制造假泄漏。
- 边界:工作量不是Renderer/Tiler百分比;原生计数器是动态多维schema,不虚构所有硬件支持。
START · 启动与生命周期
- 优先级:首批内存之后的独立阶段(待审核)。
- 可控制:基线;will/didFinish、scene连接与首屏工作0/100/300ms有界延迟;暖恢复;首屏本地/网络内容分开。
- 配方示例:主机提前准备采集 → 受控进程冷启动 → 首屏标记 → 可交互标记 → 显式结束(示例不是替换原正式轮时长/分母)。
- 对照记录:启动请求与观测在主机;App可进入回调边界;CoreProfile真实首尾;相同端点时才比较绝对值。
- 额外工具能力:回前台、页面首开、延迟初始化、首屏失败/超时、启动崩溃;不每次启动自动重放上轮负载。
- 边界:App内时钟覆盖不到System BEGIN;onAppear/DisplayLink/首个CA提交不是首帧物理呈现;不把九阶段相加成Total。
ENERGY · 能耗与设备状态
- 优先级:按指标顺序实现。
- 可控制:CPU/GPU/网络/屏幕/前台定位单变量工作;充电与连接状态记录;定位显式授权。
- 配方示例:空闲 → 单负载 → 恢复;不同来源和供电条件分开批次(示例不是替换原正式轮时长/分母)。
- 对照记录:工作账本与设备状态;源生能耗评分、放电电流、电压、温度分域。
- 额外工具能力:持续负载下的变化、拒绝权限分支、低电量/热状态下的适配。
- 边界:能耗cost不是mW;无仪器时不声称物理校准;不绕过系统定位权限或为升温无限加压。
WEB · WebView实验室
- 优先级:通用工具场景。
- 可控制:内置离线页面/受控本地服务;DOM规模、JS短长任务、图片、Canvas、缓存、路由、刷新。
- 配方示例:原生基线 → WKWebView首开 → 列表滚动 → JS负载 → 关闭释放 → 再打开(示例不是替换原正式轮时长/分母)。
- 对照记录:navigation回调、页面performance标记、JS事件、资源请求与native生命周期。
- 额外工具能力:白屏/加载失败、桥接消息、导航取消、Web内容进程终止回调、前后台恢复。
- 边界:Web内容/网络在独立进程,主进程指标不自动代表整个网页;JS时间与native时钟需显式桥接。
CRASH · 崩溃与恢复
- 优先级:通用工具场景。
- 可控制:仅自有进程:显式abort、受控断言失败、抛错但被捕获;按次预先武装。
- 配方示例:记录并落盘意图 → 倒计时/确认 → 触发一次 → 下次启动显示上轮未完成并导出(示例不是替换原正式轮时长/分母)。
- 对照记录:持久化意图/最后事件/下次启动恢复;系统崩溃报告若可得单独关联。
- 额外工具能力:采集中目标退出、异常断流、重开后新进程归属、部分文件可读性。
- 边界:意图不是崩溃成功证据;未正常结束不能自动断言crash;不默认OOM或watchdog,不永久挂死UI。
MIX · 真实业务组合
- 优先级:单指标通过后的扩展。
- 可控制:列表/图片流、上传下载、页面跳转、WebView、后台前台、资源释放的有序组合。
- 配方示例:可重复短业务配方 → 固定检查点 → 取消/恢复 → 明确终态(示例不是替换原正式轮时长/分母)。
- 对照记录:各子场景事件与实际完成量;逐进程/设备/呈现域分层。
- 额外工具能力:弱服务、长列表、图片缓存、会话恢复、重复进入退出。
- 边界:组合压力用于集成回归,不替代单变量定位或每指标验收。
6. 四类重点扩展的具体要求
6.1 网络:长期常用,不止一个请求按钮
支持HTTP/HTTPS固定载荷上下行、双向、并发、限速、连接复用/新建、上传下载进度;保留取消/重试/部分成功。受控服务端提供固定长度/内容摘要、echo、指定状态码、重定向、延迟首字节、缓慢分块、提前断开、Range与WebSocket echo/心跳。记录请求层、业务层、服务端成功接收层与系统层,不把URLSession字节直接叫网卡线速字节。
网络服务复用现有Mac环境,显式按需启动、停止后退出,限制地址/连接/流量/时限,不建常驻服务。手机到本机需要可达的受控网络地址,不能填手机自己的localhost。只用自有端点,不拿银行或用户账号服务做故障注入。ATS例外仅限必要本地测试域,不全局关闭TLS验证。HTTP故障模拟不是网络层丢包/弱信号;真正飞行模式/Wi-Fi切换单列需要配合的外部条件,不擅自改用户连接。
6.2 卡顿:让“卡在哪里、卡了多久”可解释
提供主线程有限忙任务、主线程有限等待、后台同量任务对照;设置40/100/200/500ms与周期/次数,总阻塞预算有界。长列表/图片解码/布局、图形过绘作为真实场景。实际测每次工作起止,不把计划时长当实际;UI在阻塞时不能实时响应,严格限制单次时长并预先显示。5秒级Hang独立危险档,不默认加入普通回归;无限死锁不提供常规入口。
6.3 崩溃:一次性触发、可靠恢复、不污染指标轮
显式断言/abort只终止此自有App;错误捕获与真正进程终止是不同场景。运行前显示风险、确认、写出并关闭触发意图,再触发一次;冷启动后不自动二次崩溃。OS报告可用性由实际环境决定,不在信号处理器里承诺安全写完整JSON,不把“文件没封口”一律认定为Crash。OOM/jetsam/watchdog只有独立高风险合同和真实系统证据才测试/定类,默认不执行。崩溃轮用于Perfowl退出/断流/部分保存检查,不混进正常连续性五轮以缩分母。
6.4 WebView:页面体验与跨进程边界一起测
内置离线页面保证可复现;受控联网页面验证资源加载/缓存/错误。覆盖首开、重载、前进后退、DOM列表、JS短/长任务、图片解码、Canvas、native↔JS消息往返、视图销毁与重新创建。导航完成、DOMContentLoaded、load、页面自定义可交互标记分开,不统一叫“首帧”。Web内容进程终止回调只用于检测,不能保证用公开API可精确主动终止该进程。独立Web/网络进程的工作不默认合并到宿主App进程;process-group如果未来产品支持,另立归属协议,不偷偷改当前66项scope。
7. 启动:12项全部设计,不只一个Total
启动单独准备采集再受控进程冷启动;前台返回和页面首次打开另列,不叫冷启动。App本身不能控制System Interface/系统UIKit初始化的精确时长;这几项用真实系统span与基线比较,不伪造“设定值”。
- 正常基线:最小启动工作,无网络依赖,轻量首屏与可交互标记分开。
- 控制变体:选定真实回调/首屏路径增加0/100/300ms工作,记录真实入口出口;实际耗时可能超过设定。下一轮参数须在launch前准备,不能开App读设置后把本次当冷启动。
- 单独看“有无可信端点/目标绑定”“重复获取”“对额外工作的响应”“数值独立对照”;不能只凭每轮有值判准确。
- App Launch Total沿用现有系统BEGIN→真实Initial Frame END公式,不改成App内部时间,不用onAppear/DisplayLink/下一帧补端点,不把九阶段相加。
- 未触发的条件回调保留这一指标行,只有可证明路径未发生时记not-applicable;没有证据则missing。全66项覆盖不是强迫每一启动出现全部回调。
- 启动参考“无App内详细记录”与“启用回调记录”各一个模式,评估观察开销;重型文件/网络/参考模块不在首帧前自动创建。
- 首轮开发保持现有应用生命周期;若当前SwiftUI路径不能提供某些真实delegate边界,先提独立设计变更与来源差异,不为凑九阶段改变生命周期后冒充原App。
8. 允许误差:可执行的新原则(量化细则待本稿审核)
Owner已同意“不明显偏离,允许误差”。本稿将其操作化,不再用每点与设定值完全相等或追求所有指标物理校准作为普通交付先决条件。源可信、目标scope正确、无虚构/陈旧、连续性、显式缺失与固定轮数仍是硬要求。
8.1 比较步骤
先查scope、单位、时钟与窗口,再比稳态数值/负载增量/异常峰值/恢复;不支持同口径对照时明确“对照未建立”,不算通过。PerfDog是交叉参照而非绝对真值;同源两工具相近不自动提供独立校准。先短测共存是否互相干扰,冲突则按固定配方A/B/B/A交替;交替只证场景统计,不证逐点同步,变化快的Free不能据此得出同一瞬时相等。
8.2 提议的异常筛查阈值模板
以下是本项目待审核的工程建议,不是行业标准,不写回既有合同。以同口径、对齐窗口参考R为基准,误差包络 T=max(A,B*abs(R));只有参考稳定/可比时才适用。稳态连续3个已完成1s窗口超T则“明显偏离”,整轮可比窗口超过5%落外也不接受。瞬态峰值/极端值另查,不能用均值掩盖;负值哨兵、错归属、陈旧值即时失败,不享误差豁免。窗口不足或不可比记录unknown,不能填0或删分母。
| 指标类别 | 初拟A / B | 附加要求 |
|---|---|---|
| CPU同口径占用 | 3个百分点 / 15% | 未归一化可>100%,核数对齐;不把3%写成3个百分点 |
| 进程内存 | 16MiB / 10% | 参考为同类系统API,不是申请量;resident/footprint/virtual分别审 |
| 整机Free/Available | 64MiB / 15% | 各自定义与可信来源成立;环境波动与采样时差单列,不能因此豁免来源漂移 |
| 网络采集累计量 | 256KiB / 10% | 只比相同层/方向/scope/时间窗;未知协议层开销先解决,不能机械比业务payload |
| FPS | 3fps / 10% | 参考为可比呈现统计,不是请求FPS |
| 帧耗时P50/P95 | 5ms / 15% | 同一窗口/算法;同一原序列离线复算按原取整规则而不是放宽此误差 |
| 启动 | 50ms / 20% | 仅端点语义相同的参照;不同首帧定义只作旁证;受控增量响应单独检查 |
| 线程/枚举/派生公式 | 不套通用百分比 | 创建增量/已知状态/算法结果核对;原派生公式与取整不因允许误差而改变 |
| GPU/调度/能耗与电池 | 按源能力逐项预置 | 无同口径参考只做来源/趋势/合理性;无仪器不作物理校准,缺口保留 |
有规范性来源范围的字段必须额外满足其范围;所有异常筛查保留原参考质量。应用0负载也有基线开销;合法0不自动异常。调大负载并重复单变量检查能帮助定位,但不能重复抽样直到过关。
8.3 连续性、轮数与旧合同关系
本次Owner改变数值误差方向,不等于取消连续性。建议沿用当前有效v5每项固定五轮及其原名义分母/连续性与有效性政策;时序/计数/向量/一次性启动分别用适用合同,不能把启动12项强塞成240点连续指标。启动新增五次完整独立启动的正式合同须在执行前落盘,诊断/负载增量轮不替换失败正式轮。
当前metric-stage-targets-v3.json/nonstartup-v5.json与现有门禁均未改。本稿审核后,先追加新版本并明确supersedes哪一条数值判断,历史数字/误差/分母/失败记录保留;不同版本结论不混为旧门通过。在Owner认可本稿之前不执行新阈值。
9. 66项一对一覆盖矩阵
编号N01–N54沿用既定非启动顺序;S01–S12是新增启动设计范围,不改变原54顺序。每一行的metricID/scope/unit/source/version详见同源JSON,下面逐项提供场景/对照/边界。所有行当前均为设计待审核,不是新验收通过。更多维度(逐核、GPU动态计数器)须在对应行展开实际维度清单,不能一个示例代表全部维度。
| 编号 | 指标 / metricID | scope · unit | 场景 | 控制与对照 | 边界 |
|---|---|---|---|---|---|
| N01 | 目标App CPUios.process.cpu.non_normalized | process · percent | CPU | 线程忙闲阶梯与累计进程CPU时间/实际时间窗 | 设置忙占比不是结果百分比;明确非归一化口径 |
| N02 | 物理内存占用ios.process.memory.physical_footprint | process · bytes | MEM | 触页阶梯与本进程footprint/resident分别观察 | footprint/resident不互换;释放不保证系统读数即时回到基线 |
| N03 | 驻留内存ios.process.memory.real | process · bytes | MEM | 触页阶梯与本进程footprint/resident分别观察 | footprint/resident不互换;释放不保证系统读数即时回到基线 |
| N04 | 虚拟内存ios.process.memory.virtual | process · bytes | MEM | 虚拟预留/触页切换;本进程虚拟地址空间读数 | 虚拟大小不等于申请业务量或常驻量 |
| N05 | 线程数ios.process.thread.count | process · count | CPU | 创建/退出N个常驻线程与本进程线程枚举 | 线程池与框架基线存在;GCD任务数不是线程数 |
| N06 | 目标App网络发送ios.process.network.send_rate | process · bytes-per-second | NET | 固定不压缩载荷;服务端账本/任务指标/累计来源 | 整机/进程、上下行、业务/协议层分别匹配;Web进程另列 |
| N07 | 目标App网络接收ios.process.network.receive_rate | process · bytes-per-second | NET | 固定不压缩载荷;服务端账本/任务指标/累计来源 | 整机/进程、上下行、业务/协议层分别匹配;Web进程另列 |
| N08 | 整机空闲内存ios.device.memory.free | device · bytes | MEM | host VM free与原始页大小(API可读时);触页负载 | 只比较Free;不拿Available顶替;分配量不等于整机减少量 |
| N09 | 整机可用内存候选ios.device.memory.available | device · bytes | MEM | free/external/purgeable原字段及候选算法;跨工具先核定义 | 不把可分配上限/Free当Available,仍披露candidate |
| N10 | 整机CPU总量ios.device.cpu.total | device · percent | CPU | 整机CPU状态差分;核数与归一化公式分别检查 | 自有App只贡献部分整机负载;原始与除核数结果分开 |
| N11 | 整机CPU归一化ios.device.cpu.total.normalized | device · percent | CPU | 整机CPU状态差分;核数与归一化公式分别检查 | 自有App只贡献部分整机负载;原始与除核数结果分开 |
| N12 | 逐核CPUios.device.cpu.core_usage | device · percent | CPU | 逐核系统CPU状态差分与线程阶梯 | 调度器决定落核;缺核不补0,不能指定某核精确占用 |
| N13 | 上下文切换率ios.process.scheduler.context_switch_rate | process · count-per-second | CPU | 有界锁竞争/周期等待;系统累计计数若可读 | 创建等待任务数不等于上下文切换或硬件中断次数 |
| N14 | 中断唤醒率ios.process.scheduler.interrupt_wakeup_rate | process · count-per-second | CPU | 有界锁竞争/周期等待;系统累计计数若可读 | 创建等待任务数不等于上下文切换或硬件中断次数 |
| N15 | 磁盘读取速率ios.process.disk.read_rate | process · bytes-per-second | IO | 成功读写字节账本及系统进程I/O累计值 | 逻辑调用/页缓存/物理IO分层,不能强求业务字节相等 |
| N16 | 磁盘写入速率ios.process.disk.write_rate | process · bytes-per-second | IO | 成功读写字节账本及系统进程I/O累计值 | 逻辑调用/页缓存/物理IO分层,不能强求业务字节相等 |
| N17 | 整机网络发送ios.device.network.send_rate | device · bytes-per-second | NET | 固定不压缩载荷;服务端账本/任务指标/累计来源 | 整机/进程、上下行、业务/协议层分别匹配;Web进程另列 |
| N18 | 整机网络接收ios.device.network.receive_rate | device · bytes-per-second | NET | 固定不压缩载荷;服务端账本/任务指标/累计来源 | 整机/进程、上下行、业务/协议层分别匹配;Web进程另列 |
| N19 | 屏幕刷新率ios.display.refresh_rate | foreground-device · hertz | FRAME | 设备能力/设置与外部实际refresh_interval | DisplayLink请求频率不是实际屏幕刷新率 |
| N20 | 前台FPSios.foreground.fps | foreground-device · frames-per-second | FRAME | 平滑/阻塞工作与外部实际呈现序列 | 不能用请求FPS或回调次数作物理呈现真值 |
| N21 | 单帧耗时ios.frame.time | foreground-device · milliseconds | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N22 | 帧耗时P95ios.frame.time.p95 | foreground-device · milliseconds | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N23 | 帧节奏抖动ios.frame.pacing_jitter | foreground-device · milliseconds | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N24 | 平均FPSios.frame.fps.average | foreground-device · frames-per-second | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N25 | FPS至少18占比ios.frame.fps.at_least_18_percent | foreground-device · percent | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N26 | FPS至少25占比ios.frame.fps.at_least_25_percent | foreground-device · percent | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N27 | 帧耗时至少100ms占比ios.frame.ftime_ge_100_percent | foreground-device · percent | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N28 | 帧耗时正向突增率ios.frame.ftime_positive_delta_gt_100_per_hour | foreground-device · count-per-hour | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N29 | FPS下降率ios.frame.drop_per_hour | foreground-device · count-per-hour | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N30 | FPS中位范围占比ios.frame.med_range_percent | foreground-device · percent | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N31 | 1%低帧率ios.frame.one_percent_low_fps | foreground-device · frames-per-second | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N32 | 小卡顿率ios.frame.small_jank_per_10min | foreground-device · count-per-10-minutes | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N33 | 卡顿率ios.frame.jank_per_10min | foreground-device · count-per-10-minutes | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N34 | 大卡顿率ios.frame.big_jank_per_10min | foreground-device · count-per-10-minutes | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N35 | 卡顿时间占比ios.frame.stutter_percent | foreground-device · percent | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N36 | 微卡顿率ios.frame.tiny_jank_per_10min | foreground-device · count-per-10-minutes | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N37 | 流畅度ios.frame.smooth | foreground-device · score | FRAME | 真实呈现序列离线按该metric公式重算;记录实际阻塞 | 同源重算只证算法;跨源同定义才证一致性;低样本不补值 |
| N38 | GPU渲染利用率ios.foreground.gpu.renderer_utilization | foreground-device · percent | GPU | 几何/像素工作量阶梯与可用外部GPU源 | 不是预置GPU百分比;动态计数器逐维列单位,unsupported不冒充0 |
| N39 | GPU平铺利用率ios.foreground.gpu.tiler_utilization | foreground-device · percent | GPU | 几何/像素工作量阶梯与可用外部GPU源 | 不是预置GPU百分比;动态计数器逐维列单位,unsupported不冒充0 |
| N40 | GPU整体利用率ios.foreground.gpu.device_utilization | foreground-device · percent | GPU | 几何/像素工作量阶梯与可用外部GPU源 | 不是预置GPU百分比;动态计数器逐维列单位,unsupported不冒充0 |
| N41 | GPU原生计数器ios.device.gpu.counter.source_native | device · source-native-count | GPU | 几何/像素工作量阶梯与可用外部GPU源 | 不是预置GPU百分比;动态计数器逐维列单位,unsupported不冒充0 |
| N42 | 总能耗评分ios.process.energy.total_cost | process · source-native-cost | ENERGY | 对应CPU/GPU/网络/定位/屏幕工作与源生cost | cost不是电功率;定位需真实授权;overhead不设置目标值 |
| N43 | CPU能耗评分ios.process.energy.cpu_cost | process · source-native-cost | ENERGY | 对应CPU/GPU/网络/定位/屏幕工作与源生cost | cost不是电功率;定位需真实授权;overhead不设置目标值 |
| N44 | GPU能耗评分ios.process.energy.gpu_cost | process · source-native-cost | ENERGY | 对应CPU/GPU/网络/定位/屏幕工作与源生cost | cost不是电功率;定位需真实授权;overhead不设置目标值 |
| N45 | 网络能耗评分ios.process.energy.network_cost | process · source-native-cost | ENERGY | 对应CPU/GPU/网络/定位/屏幕工作与源生cost | cost不是电功率;定位需真实授权;overhead不设置目标值 |
| N46 | 定位能耗评分ios.process.energy.location_cost | process · source-native-cost | ENERGY | 对应CPU/GPU/网络/定位/屏幕工作与源生cost | cost不是电功率;定位需真实授权;overhead不设置目标值 |
| N47 | 显示能耗评分ios.process.energy.display_cost | process · source-native-cost | ENERGY | 对应CPU/GPU/网络/定位/屏幕工作与源生cost | cost不是电功率;定位需真实授权;overhead不设置目标值 |
| N48 | 能耗服务开销评分ios.process.energy.overhead | process · source-native-cost | ENERGY | 对应CPU/GPU/网络/定位/屏幕工作与源生cost | cost不是电功率;定位需真实授权;overhead不设置目标值 |
| N49 | 整机热状态ios.device.thermal.status | device · enum-raw | ENERGY | 受控负载+ProcessInfo热状态与源枚举分别观察 | 不同服务热状态不假定一一映射;严重状态立即停止加压 |
| N50 | 电池电压ios.device.battery.voltage | device · millivolt | ENERGY | 供电/充电记录;同一快照原字段及换算;仪器可选 | 无仪器不作独立校准;USB充电不当放电;原始温度非摄氏度 |
| N51 | 电池放电电流ios.device.battery.current | device · milliampere | ENERGY | 供电/充电记录;同一快照原字段及换算;仪器可选 | 无仪器不作独立校准;USB充电不当放电;原始温度非摄氏度 |
| N52 | 电池放电功率ios.device.battery.power | device · milliwatt | ENERGY | 供电/充电记录;同一快照原字段及换算;仪器可选 | 无仪器不作独立校准;USB充电不当放电;原始温度非摄氏度 |
| N53 | 电池原始温度ios.device.battery.temperature_raw | device · source-native | ENERGY | 供电/充电记录;同一快照原字段及换算;仪器可选 | 无仪器不作独立校准;USB充电不当放电;原始温度非摄氏度 |
| N54 | 电池摄氏温度ios.device.battery.temperature_celsius | device · degree-celsius | ENERGY | 供电/充电记录;同一快照原字段及换算;仪器可选 | 无仪器不作独立校准;USB充电不当放电;原始温度非摄氏度 |
| S01 | 观测到进程运行耗时ios.process.startup.observed_running_ms | process · milliseconds | START | 主机请求/进程观测;保留轮询误差界 | 不是首帧;App内标记不替代主机观测 |
| S02 | 观测到前台就绪耗时ios.process.startup.foreground_ready_ms | process · milliseconds | START | 主机请求/前台观测与App活跃回调并列 | 活跃回调不是外部前台检测的同一时刻 |
| S03 | 启动总耗时ios.process.startup.app_launch_total_ms | process · milliseconds | START | 基线及回调/首屏额外工作;CoreProfile原始端点独立复算 | 起点必须真实系统BEGIN;缺失不补0;不同首帧定义不直接对等 |
| S04 | 系统接口初始化ios.process.startup.system_interface_ms | process · milliseconds | START | 同包基线与真实生命周期span;不伪造系统阶段 | App不直接控制该系统阶段耗时;OS/冷暖缓存记录,缺失保留 |
| S05 | 静态运行时初始化ios.process.startup.static_runtime_ms | process · milliseconds | START | 同包基线与真实生命周期span;不伪造系统阶段 | App不直接控制该系统阶段耗时;OS/冷暖缓存记录,缺失保留 |
| S06 | UIKit初始化ios.process.startup.uikit_initialization_ms | process · milliseconds | START | 同包基线与真实生命周期span;不伪造系统阶段 | App不直接控制该系统阶段耗时;OS/冷暖缓存记录,缺失保留 |
| S07 | UIKit场景创建ios.process.startup.uikit_scene_creation_ms | process · milliseconds | START | 同包基线与真实生命周期span;不伪造系统阶段 | App不直接控制该系统阶段耗时;OS/冷暖缓存记录,缺失保留 |
| S08 | willFinishLaunching回调ios.process.startup.will_finish_launching_ms | process · milliseconds | START | 相应真实回调入口/出口及0/100/300ms额外工作 | 回调未发生须有证据才记不适用;不人为制造系统span |
| S09 | didFinishLaunching回调ios.process.startup.did_finish_launching_ms | process · milliseconds | START | 相应真实回调入口/出口及0/100/300ms额外工作 | 回调未发生须有证据才记不适用;不人为制造系统span |
| S10 | sceneWillConnect回调ios.process.startup.scene_will_connect_ms | process · milliseconds | START | 相应真实回调入口/出口及0/100/300ms额外工作 | 回调未发生须有证据才记不适用;不人为制造系统span |
| S11 | sceneWillEnterForeground回调ios.process.startup.scene_will_enter_foreground_ms | process · milliseconds | START | 相应真实回调入口/出口及0/100/300ms额外工作 | 回调未发生须有证据才记不适用;不人为制造系统span |
| S12 | 首帧渲染阶段ios.process.startup.initial_frame_rendering_ms | process · milliseconds | START | 首屏复杂度/绘制工作阶梯与外部真实span | 提交绘制/可交互/首次呈现三者分列 |
10. 记录与导出协议
每轮目录只新增,结构拟为:plan.json / effective-config.json / events.jsonl / 启用的reference文件 / summary.json / manifest.json。真实私有标识只在私有目录;公开脱敏导出必须另存,不回改原件。summary保存requested/actual/cancelled/failed、参考能力/权限、环境与理由;partial不能补写completed。
- 事件包含schema、时钟域、单调时间、阶段、负载类型、计划与实际量、单位、结果/错误;原值精度不为界面取整而丢失。
- 完整封口只在flush/关闭成功并记录文件长度/SHA后成立;失败文件保留可读前缀。Crash意图提前落盘,不承诺Crash后还能封口。
- 网络文件只存受控载荷种子/长度/摘要,不默认保存全部大payload;导出主机用有界流式搬运。截图默认OFF,不用来伪装帧真值。
- 自有App/Perfowl/PerfDog分别存原件与版本;对照报告引用三方SHA与运行关联,不改原导出。导入格式按实际安装版本适配,不假设当前Info里的1.0就是产品发布版本。
- 自动配方在手机自己计时执行;Mac协调器只负责准备/显式启动/状态等待/导出;不默认依赖WDA/XCTest或截图点击轮询。长期工具可手动操作、保存配方、离线运行,不依赖本次AI上下文。
11. 架构决策 ADR-TESTLAB-01(Proposed)
决策者:Owner。 选择原工程模块化:ScenarioDefinition → RunCoordinator → WorkloadEngine → EventRecorder/ReferenceAdapter → RunStore/Export;SwiftUI只显示状态与发意图,资源持有在受控worker中。一个Run只启动所需模块,普通/验收/危险诊断三种运行模式显式区分。
| 方案 | 成本/优点 | 风险 | 结论 |
|---|---|---|---|
| 继续在原大View中加按钮和隐式编译开关 | 首次写得快 | 参数/参考/生命周期耦合,容易重演配置不生效 | 不选 |
| 原工程同身份,逐场景抽模块、统一运行协议 | 复用旧代码与安装,按项交付,容易隔离参考开销 | 需保持旧参考兼容与来源记录 | 推荐 |
| 另建新App/独立SDK/新常驻服务 | 表面干净 | 多版本安装、证据分裂、额外环境与维护 | 不选 |
工作负载与参考采样解耦,所有参考默认OFF;不得把验证代码注入商店App或Perfowl目标进程。参考API不可读时显示unavailable,并保留错误,不用另一含义的API偷偷顶替。
12. 资源与停止策略(设计预算,尚未实测)
- 复用现有Swift/Xcode/Python环境、单个手机安装;不新建常驻环境。模块按需加载,复杂度O(事件数/传输字节),有界队列/批写,数据量大时流式导出,不重复全库扫描。
- 一次一个基础负载;组合配方显式限定并发。主线程不做日志编码/文件哈希/大图片解码;卡顿测试是明确例外且有最长阻塞。
- 普通场景默认≤180s、上限600s;网络默认≤50MiB、可审核上限512MiB/并发4;内存默认≤80MiB、普通档上限min(256MiB,物理内存10%);所有分配失败立即停,不当OOM成功。需要更大负载另存高风险配方,不静默抬上限。
- 持续负载出现memory warning、热状态serious/critical、资源配额或写失败时立即停止并记录partial,不为了目标数值继续加压。电池场景记录供电,USB与无线/放电批次分开。普通档禁止故意追求热极限或耗尽磁盘。
- App参考+记录开销设计目标:空闲额外CPU≤2个百分点,额外常驻≤20MiB,普通日志≤10MiB/轮;这不是当前通过事实,也不修改旧1ms合同。真实设备按同包OFF/ON受控对照实测平均/p95处理延迟、CPU、RSS、队列峰、I/O、首响应与停止收尾,不把合成微基准变成无限阻塞。
- 开始/批末/长测30分钟检查空间,主机≥10GiB且≥预计新增两倍,手机自有工作目录有上限;只清已明确可重建/已完成且无引用的测试payload缓存。原始失败/待分析/引用证据不自动按年龄删除;清理预览列范围/字节/理由,必要摘要与验收证据先确认留存策略。用户已删除旧归档的事实不反向改写为已保留。
13. 分阶段交付与验收
| 阶段 | 本阶段产物 | 开发退出条件 | 不代表什么 |
|---|---|---|---|
| D0 本稿 | 66项映射、场景库、可点原型、误差建议 | Owner逐项审核范围与边界 | 不代表任何新App/指标通过 |
| D1 公共骨架+当前内存 | 配方、轻量记录、停止恢复、内存控制;旧参考按需适配 | 当前场景可重复、实际量可导出、开销及安装核对成立 | 不等于Free来源/正式五轮已通过 |
| D2 当前Free指标交付 | 来源/异常排查+新合同正式五轮+签名同址覆盖 | 数值/质量/五轮/证据/提交发布闭环完成 | 不跳过来源漂移或其它指标 |
| D3 启动专阶段(提议插入) | 12项启动场景与逐项结果 | 基线/边界/控制变体成立,Total与阶段分别裁定 | 缺阶段不伪造0;有Total不是12/12全部通过 |
| D4 原54余项按序推进 | 每项所需的网络/调度/IO/帧/GPU/设备模块 | 当前指标正式闭环后推进下一项 | 不因场景库按钮齐全宣布全指标完成 |
| D5 长期工具整体验收 | WebView/崩溃/组合业务、兼容矩阵、用户说明与回归配方 | 手机与Mac单版本安装;正常/异常/恢复与数据导出测试 | 不承诺所有iOS/机型和物理校准已完成 |
Owner要求本轮包含启动,故提议在当前Free关闭后插入启动专阶段,原非启动54相对次序不变;插入位置是本稿审核项,尚未执行。不能因开发通用工具绕过当前指标的顺序门。
每项正式验收后依旧窄提交、双远程同提交(GitLab仅一次)、更新知识库/开发记录/归档并发布网站;当前设计待审核不推送共享WIP、不发布草稿或发送“工具批次完成”通知。测试包后续同址覆盖,不并行安装;公证按Owner此前暂缓测试包的要求与最终正式分发要求分开说明。
14. Owner审核清单
- 66项完整映射是否漏掉实际需要的指标或边界?不把设计覆盖看作全部可准确校准。
- 一级页面与11类场景是否符合长期工具使用习惯?是否需要优先保留某类业务配方?
- 误差包络建议是否接受;未匹配scope/时钟/来源时不能用宽容差充当通过。
- 当前内存之后插入启动专阶段是否认可;其它54相对顺序不变。
- 崩溃/长Hang等危险场景采用一次确认、可恢复、不默认自动执行;WebView跨进程单独展示。
- 本地受控网络服务按需运行、不常驻;普通资源上限、导出/保留/清理方式是否合适。
本轮停在设计审核,不自动进入实现。 审核意见修改本稿形成定稿,再拆首个实施合同。未来实施追加带日期更新,不回改历史实测或旧验收报告。
15. 证据与来源
E1:本地Registry/合同/Fixture静态盘点;覆盖表生成检查不是E2构建或E4真机验收。现有阶段状态见内部handoff与原v5台账,不能从本稿推导新通过数。
- 当前Registry:
MacApp/Sources/PerfowlCore/PerformanceModels.swift;对应快照:site/data/metric-contracts.json;原顺序:internal/acceptance/metric-stage-targets-v3.json。精确摘要见配套JSON。 - 当前Fixture:
fixtures/ios/PerformanceProbeFixture/PerformanceProbeFixture/PerformanceProbeFixtureApp.swift,本轮只读;原参考与失败不删除。 - 启动Total口径、启动观测、九阶段、会话完整性与证据。
- Apple:启动顺序与预热:系统阶段与App可进入回调不等同。
- Apple:CADisplayLink帧率请求:系统能力与策略影响实际回调频率,不能以请求频率作真值。
- Apple:URLSession请求体字节:包含协议framing/encoding,不同层业务字节分别记账。
- Apple:Web内容进程终止回调:Web内容进程独立于宿主App,不能混淆scope。
- Apple:热状态:serious/critical为停止加压条件。
- PerfDog官方指标说明:同名指标不自动同口径;本机实际支持项和导出尚待逐项核对。
Owner审核结果 · 2026-10-15
Owner已审核通过并授权开发与验收。以上审核稿内容保留;正式执行依据转入实施与验收总计划。本次批准不是App或指标已通过。