外观
StartupTiming:12 项指标拆分索引(兼容入口)
本页不再拥有 metric ownership。 从 2026-09-18 起,StartupTiming 原先合并的 12 个进程级指标按语义拆分为三个独立 Canonical 页,公式、质量语义、证据与版本历史均以下面三页为准;本页只保留历史入口与拆分说明,不再重复维护口径。
| 分层 | 指标 | 新 Canonical 页 |
|---|---|---|
| Observation Metrics:轻量外部观测 | observed_running_ms、foreground_ready_ms | Startup Observation |
| Primary Metric:启动主结果 | app_launch_total_ms | App Launch Total |
| Diagnostic Phase Metrics:九阶段诊断分解 | 9 个 phase 指标 | Startup Phases |
三个新页各自包含完整的十要素:定义、metricID/scope/unit/source、系统原理、系统服务与调用实现、原始字段、精确公式、Clock/Window/Freshness、质量语义、当前证据与平台覆盖、常见误区、Legacy、版本历史。领域总览见 iOS 应用启动性能总览,历史技术决策见 Startup 技术演进与实验。
以下内容为拆分前 v21~v47 版本沉淀的背景说明,继续保留供历史查阅,不代表当前权威口径;如与三个新 Canonical 页冲突,以新页为准。
1. 指标分层
1.1 Observation Metrics:轻量外部观测
| 指标 | metricID | 数据源 | 含义 |
|---|---|---|---|
| Process Observed Running | ios.process.startup.observed_running_ms | lifecycle-observer | 从 Launch Request 到主机首次观测目标进程运行 |
| Foreground Ready | ios.process.startup.foreground_ready_ms | lifecycle-observer | 从 Launch Request 到主机首次观测目标进入前台 |
这两个指标使用主机 monotonic 时间,并受 lifecycle polling 周期影响;报告会保留 pollingErrorUpperBoundMs。它们适合作为粗粒度启动观测与 CoreProfile 不可用时的降级信息,不是 First Frame,也不能冒充 App Launch Total。
1.2 Primary Metric:App Launch Total
| 指标 | metricID | 数据源 | 定义 |
|---|---|---|---|
| App Launch Total | ios.process.startup.app_launch_total_ms | CoreProfile kdebug lifecycle | System Interface BEGIN → Initial Frame END 的绝对端点跨度 |
核心公式:
Launch Total 不是九阶段之和。 中间阶段存在并行、交叠、条件回调;只要首尾端点成立,中间阶段本轮缺失并不会自动使 Total 失效。
1.3 Diagnostic Phase Metrics:九阶段可观测分解
| # | 指标 | metricID | 性质 |
|---|---|---|---|
| 1 | System Interface Initialization | ios.process.startup.system_interface_ms | 必需;Total 起点所在阶段 |
| 2 | Static Runtime Initialization | ios.process.startup.static_runtime_ms | 必需 |
| 3 | UIKit Initialization | ios.process.startup.uikit_initialization_ms | 必需 |
| 4 | UIKit Scene Creation | ios.process.startup.uikit_scene_creation_ms | 必需 |
| 5 | willFinishLaunchingWithOptions() | ios.process.startup.will_finish_launching_ms | 条件回调 |
| 6 | didFinishLaunchingWithOptions() | ios.process.startup.did_finish_launching_ms | 核心生命周期阶段 |
| 7 | sceneWillConnectTo() | ios.process.startup.scene_will_connect_ms | 条件回调 |
| 8 | sceneWillEnterForeground() | ios.process.startup.scene_will_enter_foreground_ms | 条件回调 |
| 9 | Initial Frame Rendering | ios.process.startup.initial_frame_rendering_ms | 必需;Total 终点所在阶段 |
每个阶段值来自完整 BEGIN / END span 的真实时间差或同名 span 累计值;缺失时不使用邻居时间插值,也不以 0 填充。
2. 数据源与启动语义
深度启动分析使用:
text
processcontrol
└── 发起受控 process-cold launch
coreprofilesessiontap
└── kdebug lifecycle stream
├── 0x1F07 dyld / libSystem
├── 0x2BDC lifecycle transition
├── 0x2B87 UIKit lifecycle signpost
└── 0x31CA initial frame endpointPerfowl 的受控启动确保目标旧进程被替换,但不宣称清空 dyld/page cache,也不宣称消除 iOS prewarming。因此知识库统一使用:
controlled process-cold launch / 受控进程冷启动
而不使用“设备级绝对冷启动”。
3. Total Provenance:主结果来源质量
Total 的“有没有值”和“这个值怎么得到”必须分开表达。
| 状态 | totalQuality / source | 语义 | 使用建议 |
|---|---|---|---|
| Clean Endpoint | candidate + coreprofile-endpoint | System BEGIN 与 Frame END 清晰成对 | 主结果 |
| Repaired Exact | repaired + frame-end-repaired | 真实首帧终点存在,普通 opener 丢失,经生命周期锚点重新配对 | 可用于报告,必须披露 repaired |
| Repaired Ambiguous | repaired + frame-end-repaired-ambiguous | 存在多个可能终点,保留最早候选 | 候选值,不得冒充清晰端点 |
| Start Substituted | repaired + start-substituted | System BEGIN 缺失,使用最早可用 dyld 起点替代 | 保留真实时间戳,但不等同 clean Total |
| Missing | missing | 关键终点/起点缺失且没有诚实恢复路径 | 不给值 |
repaired 不表示“估算”。当前修复仍使用真实内核 timestamp;但只要不是清晰端点配对,就必须把 provenance 暴露给用户。
4. Phase Coverage:阶段覆盖质量
阶段展示使用三个基本状态:
- captured / candidate:完整 span,存在真实耗时;
- not-applicable:有独立证据表明本轮启动路径没有触发该条件回调;
- missing / value-less partial:本轮没有形成可信 span。
Workbench 只渲染有真实值的阶段,避免大量空行;但不会隐藏缺失事实。底部始终披露:
text
本轮捕获 X/9 个启动阶段;
N 个本轮未捕获(不展示、不补值);
M 个为该 App 未触发的条件回调。九阶段完整度是诊断覆盖度,不是 Total 的定义本身。
5. Transport Integrity:采集完整性证据
启动报告还维护与阶段覆盖正交的数据完整性证据。
Host lossFree
表示主机侧 live queue / 消费链未发生已知丢弃。它不能证明设备内核缓冲、CoreProfile tap 或设备→主机之前的所有环节完全无损。
deviceLostEventMarkerCount
来自内核 TRACE_LOST_EVENTS 标记扫描:
>0:本轮明确看到设备侧丢事件证据;0:本轮没有观测到 loss marker;nil:本记录未执行该扫描。
特别注意:0 不等于整链无损;同时,9/9 阶段完整也可能与非零 marker 共存,因为 marker 可能对应启动窗内其他 kdebug 事件。
6. 三个质量维度必须分开
一份 Startup 结果应该这样读:
text
Total Provenance
clean / repaired / missing
Phase Coverage
X/9 captured
N missing
M not-applicable
Transport Integrity
host lossFree
device loss markers例如:
text
Total = 1.83 s, clean
Coverage = 6/9
Device loss markers = 0表示 Total 有清晰端点,但本轮阶段诊断只有 6 个真实值;不能把它简化成“Startup 成功/失败”一个布尔状态。
7. Observation Metrics 的误差边界
Process Observed Running 和 Foreground Ready 的计算使用 host receive monotonic clock:
text
ObservedRunningMs
= observedRunningHostNs - launchRequestedHostNs
ForegroundReadyMs
= observedForegroundHostNs - launchRequestedHostNs它们同时提供轮询误差上界,因此应理解为“外部观察到状态变化所需时间”。这两个指标与 CoreProfile 的 mach timestamp 生命周期跨度不是同一种精度合同,不应做毫秒级逐点等价比较。
8. 测试与统计建议
日常版本回归建议:
- 使用同设备、同 OS、同 App 版本形态和同一受控 process-cold 场景;
- 连续采集多轮,首轮单独保留,不机械删除;
- 对稳定同场景轮次统计 P50 / P90,并同时记录 Total availability;
- 分开统计 clean Total 与 repaired Total 数量;
- 阶段分析同时记录 9/9、≥6/9 等 coverage,而不是只看一个平均启动时间。
建议报告至少包含:
text
First Round
Total P50 / P90
Clean Total Rate
Repaired Total Rate
Total Missing Rate
Phase Coverage Distribution
Device Loss Marker Runs9. 性能基线:优先做版本回归,不做一刀切
1200 ms、2000 ms、didFinish 300 ms 等值可以作为团队参考线,但不是所有 App、设备和业务场景通用的物理标准。
更稳妥的准入方式是:
text
同设备 + 同 OS + 同场景 + 同目标版本形态比较:
- Total P50 / P90 回归比例;
- Initial Frame / didFinish 等关键阶段回归;
- clean / repaired / missing 比例是否恶化;
- Phase Coverage 是否异常下降。
只有团队已经冻结业务 SLA 时,才把固定毫秒数升级为正式 Pass / Fail 门槛。
10. 常见问题
为什么某些 scene 阶段没有显示?
有两种完全不同的可能:App 本轮路径没有发对应生命周期 signpost,或信号在设备侧没有被观测到。Perfowl 只有在具备足够证据时才标 not-applicable;证据不足保持 missing。详情见启动观测与数据完整性。
为什么 Total 有值但只有 6/9?
因为 Total 由首尾端点直接计算,不是阶段求和。中间阶段缺失会降低诊断 coverage,但不自动抹掉已经存在的真实端点跨度。
为什么 Perfowl 与手工秒表不完全一致?
Perfowl 的核心 Total 使用系统生命周期与首帧端点;手工秒表往往从手指动作开始,并可能等到异步网络内容全部加载。二者终点定义不同。首帧之后的业务可用性应独立测量。
技术溯源与十要素契约
| 要素维度 | 详细规范与技术证据 |
|---|---|
| 定义与概念 | 一组 12 项进程级 Startup 指标:2 项 lifecycle observation、1 项 Launch Total、9 项可观测阶段诊断 |
| metricID | ios.process.startup.*,具体 12 项见本文 §1 |
| scope | process;目标 App / 目标 generation 专属 |
| 单位 (unit) | milliseconds;质量/coverage 为独立元数据,不折算成 ms |
| 底层系统服务与通信通道 | processcontrol、coreprofilesessiontap、lifecycle observer,经 DTX / Provider 路线 |
| 采集命令与调用实现 | ProcessControl 发起 controlled process-cold;CoreProfile lifecycle decoder 解码 kdebug;observer 提供外部 running/foreground 观测 |
| 计算方式与精确公式 | Total = System Interface BEGIN → Initial Frame END;阶段 = 完整 span duration;Observation = host observed - launch requested |
| 数据质量语义 (含 missing) | Total provenance、Phase Coverage、Transport Integrity 三维正交;missing 不补 0,repaired 必须披露来源 |
| 当前校准状态与证据等级 (E1~E4 ) | iOS 17 已有多轮 E4 启动采集与缺失取证;跨版本 Foundation E4 不自动外推为九阶段语义 E4,具体版本支持需独立矩阵 |
| 兼容/回放边界 | 历史 Startup Session 继续按其原始 profile / quality 回放;新知识口径不改写历史证据 |
版本历史
| 版本 | 日期 | 修订要点 |
|---|---|---|
| v48(拆分,本页降级为索引) | 2026-09-18 | 本页不再拥有任何 metric ownership;12 项指标按 Observation / Total / Phase 三层拆分为 app-launch-total.md、startup-observation.md、startup-phases.md 三个独立 Canonical 页,公式与质量语义不再在本页维护。 |
| v47 | 2026-09-17 | 重构为 Observation / Total / Phase 三层;正式收录 Total provenance、端点修复、coverage 与 device loss marker;“绝对冷启动”统一改为 controlled process-cold;移除首轮必舍弃与统一 Pass/Fail 绝对化叙事。 |
| v46 | 2026-09-15 | 面向性能测试工程视角重构九阶段说明。 |
| v45 | 2026-09-14 | 规范条件阶段质量语义与 N/A。 |
| v21 | 2026-09-07 | 确立 Mach 时钟与区间耗时算法。 |