From c4ed6c1116783644b577e854dd5afdf0b5295d1e Mon Sep 17 00:00:00 2001 From: Junhui Chen Date: Tue, 9 Sep 2025 12:28:18 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E7=9B=B2=E7=9B=92?= =?UTF-8?q?=E4=B8=BB=E8=B7=AF=E5=BE=84=E6=80=A7=E8=83=BD=E5=9F=BA=E7=BA=BF?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=8C=87=E5=8D=97=E5=8F=8A=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- specs/perf_baseline.md | 149 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 specs/perf_baseline.md diff --git a/specs/perf_baseline.md b/specs/perf_baseline.md new file mode 100644 index 0000000..8b0e086 --- /dev/null +++ b/specs/perf_baseline.md @@ -0,0 +1,149 @@ +# perf-1 基线测试指南与记录模板 + +更新时间:2025-09-09 12:25 +08 + +本文件用于后续进行盲盒主路径的性能基线采集与分析,包含目标范围、事件口径、采集步骤、分析方法、优化建议以及可直接填写的记录模板。 + +--- + +## 1. 目标与范围 +- 场景 A(Image 盲盒):冷启动 → 准备(Unopened)→ 开启 → 展示图片 +- 场景 B(Video 盲盒):冷启动 → 准备(Unopened)→ 开启 → 播放视频 +- 输出:关键阶段耗时(T_*)、帧率/掉帧、CPU 热点、潜在瓶颈与改进建议 + +## 2. 现有埋点(OS Signpost) +Perf 工具位于 `wake/Core/Diagnostics/Performance.swift`(`subsystem: app.wake`,`category: performance`)。以下事件可在 Instruments 的 OS Signpost 中看到: + +- 视图(`wake/Features/BlindBox/View/BlindBoxView.swift`) + - `BlindBox_Appear` + - `BlindBox_Status_Unopened` + - `BlindBox_Status_Preparing` + - `BlindBox_Opening_Begin` + - `BlindBox_Opening_ShowMedia` +- 视图模型(`wake/Features/BlindBox/ViewModel/BlindBoxViewModel.swift`) + - `BlindVM_Load_Begin` / `BlindVM_Load_End` + - `BlindVM_Bootstrap_Done` + - `BlindVM_Poll_Single_Yield` / `BlindVM_Poll_List_Yield` + - `BlindVM_Open`(begin/end 包裹 openBlindBox 调用) + +## 3. 指标口径(阶段耗时定义) +- `T_bootstrap = BlindBox_Appear → BlindVM_Bootstrap_Done` +- `T_ready = 首次出现 BlindBox_Status_Unopened`(或 `.Preparing → .Unopened` 的跃迁) +- `T_open_api = BlindVM_Open(begin) → BlindVM_Open(end)`(开盒 API 往返) +- `T_open_anim = BlindBox_Opening_Begin → BlindBox_Opening_ShowMedia`(动画到媒体展示) +- `T_prepare_media`(建议新增埋点后再测,见第 7 节) + +建议阈值(参考): +- Image:`T_bootstrap ≤ 800ms`,`T_prepare_media ≤ 300ms` +- Video:`T_bootstrap ≤ 1200ms`,`首帧可播放 ≤ 1.0s` +- Core Animation:每 1 秒内掉帧 < 3;主线程占用峰值 < 80% + +## 4. 环境与准备 +- 设备:实体机(建议 iPhone 13 及以上),保持温度和电量稳定 +- 构建:Release/Profile(Xcode → Product → Scheme → Edit Scheme → Run = Release) +- 关闭调试开关(如 Metal API Validation) + +## 5. 数据采集(Xcode Instruments) +1) Xcode 菜单 `Product → Profile`,选择模板: + - OS Signpost(主时间轴) + - Time Profiler(CPU/主线程占用) + - Core Animation(FPS 与掉帧) + - 选配:Network(若要看请求耗时) +2) 运行路径: + - 场景 A(Image) + - 冷启动 App → 进入 `BlindBoxView` → 等待状态到 Unopened → 点击开启 → 等待图片展示 → 停止录制 + - 场景 B(Video) + - 冷启动 App → 进入 `BlindBoxView` → 等待状态到 Unopened → 点击开启 → 视频开始播放 → 停止录制 +3) 标注与导出: + - 在 OS Signpost 轨道上对齐事件(见第 2 节),测量 T_* 并记录 + - 导出 A/B 两条 trace 作为基线归档 + +## 6. 可选 CLI(xctrace) +- 查看模板与设备: +```bash +xcrun xctrace list templates +xcrun xctrace list devices +``` +- 采集 Time Profiler(将占位符替换为实际 Bundle ID): +```bash +xcrun xctrace record \ + --template "Time Profiler" \ + --output "~/Desktop/wake_timeprofiler.trace" \ + --launch com.your.bundle.id +``` + +## 7. 分析方法 +- OS Signpost:筛选 `subsystem = app.wake`,`category = performance`,沿时间轴读取 T_*。 +- Time Profiler:关注主线程热点(Lottie 渲染、SVG 绘制、SwiftUI 布局、图片解码、AVAsset 初始化等)。 +- Core Animation:查看帧时间直方图与掉帧分布,对应时间截面回到 Time Profiler 交叉验证 CPU 热点。 + +## 8. 建议新增埋点(便于下一轮更精细分析) +- `prepareMedia()`(`BlindBoxViewModel`)中增加 begin/end: + - 图片:`BlindVM_PrepareMedia_Image` + - 视频:`BlindVM_PrepareMedia_Video` +- `BlindBoxView` 的开启动画链路,如需更细,可在 `BlindBox_Opening_Begin → BlindBox_Opening_ShowMedia` 之间插入阶段性事件(例如某帧/进度阈值)。 + +## 9. 常见瓶颈与建议 +- 图片解码在主线程: + - 现状:`prepareMedia()` 标注了 `@MainActor`,`UIImage(data:)` 可能阻塞主线程。 + - 建议:使用后台队列/`Task.detached(priority: .userInitiated)` 解码,回主线程赋值;或使用 `CGImageSource` 增量解码。 +- AVAsset 初始化与尺寸计算: + - 建议:使用异步属性加载(如 `await asset.load(.tracks)`),后台计算宽高比后再回主线程设置。 +- Lottie 渲染: + - 建议:仅在可见时播放(当前已实现),检查 JSON 体量与层数,必要时优化资源。 +- SVG 渲染: + - 建议:大面积静态背景预栅格化为 PNG,交互区域保留矢量;或增加缓存层。 +- OnBoarding 去重: + - 现状:用 `uiImage.pngData()` 做去重,计算较重。 + - 建议:改用轻量哈希(缩略图 + 平均哈希/pHash)或文件 URL/尺寸 + 字节总量近似判重。 +- 网络与缓存: + - 建议:图片设置合理 `URLCache`;视频首帧使用低码率预览或占位图,降低等待感;网络层收集 `URLSessionTaskMetrics` 做 RTT/吞吐量观测。 + +## 10. 记录模板(直接复制并填写) +```markdown +# perf-1 基线(日期:____ / 设备:____ / 系统:____ / 构建:Release) + +## 场景 A(Image) +- T_bootstrap:____ ms +- T_ready:____ ms +- T_open_api:____ ms +- T_open_anim:____ ms +- T_prepare_media(若有):____ ms +- Core Animation(平均 FPS / 掉帧):____ / ____ +- Time Profiler 热点(主线程 Top3): + - 1) ____(____%) + - 2) ____(____%) + - 3) ____(____%) +- 结论与问题: + +## 场景 B(Video) +- T_bootstrap:____ ms +- T_ready:____ ms +- T_open_api:____ ms +- T_open_anim:____ ms +- T_prepare_media(若有):____ ms +- Core Animation(平均 FPS / 掉帧):____ / ____ +- Time Profiler 热点(主线程 Top3): + - 1) ____(____%) + - 2) ____(____%) + - 3) ____(____%) +- 结论与问题: + +## 归纳与下一步 +- 瓶颈总结: +- 优化优先级(P0/P1/P2): +- 行动项: +``` + +## 11. 下次继续(执行清单) +- 采集 A/B 各 1 条 trace 并填写第 10 节模板 +- (可选)为 `prepareMedia()` 增加图片/视频的 begin/end 埋点 +- 将图片解码移至后台线程,回主线程赋值 +- 使用 AVAsset 异步加载 track/时长并后台计算尺寸 +- 检查 Lottie/SVG 资源与渲染负载(必要时优化) +- 调整 OnBoarding 去重逻辑,避免 `pngData()` 重计算 +- 配置 `URLCache` 与视频首帧占位方案 + +--- + +附注:本指南依赖现有 `Perf` 工具(`Performance.swift`)与相关事件;若需要我直接提交“后台解码 + 细粒度埋点”的实现,请在下次迭代时告知,我会以最小改动提交补丁。