关于 auditing-compose-performance
面向 Android 开发者的 Compose 性能审计技能,按测量、诊断、修复、验证四阶段组织工作,并要求产出书面审计报告。
主要功能
- 01四阶段审计流程
- 02基于真实设备测量
- 03借助编译器与运行时诊断
- 04逐项应用定向修复
- 05生成基线配置并设 CI 门禁
- 06记录审计报告
适用场景
- 01排查应用整体卡顿
- 02规划性能冲刺
- 03为发布设置性能门禁
- 04为代码库新成员提供性能审计流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ skydoves/ compose-performance-skills --skill auditing-compose-performance安装后在 Claude Code 中输入/auditing-compose-performance确认已加载。
详细介绍
审计 Compose 性能——Measure → Diagnose → Fix → Verify 编排器
这是 compose-performance-skills 库中最高级别的入口。当开发者描述的症状比较笼统时——“应用感觉很卡”“到处滚动都不流畅”“我们要开始一轮性能优化”“我们到底该从哪里开始?”——Claude 就会从这里开始。这个编排器不会取代 25 个针对特定问题的技能,而是通过四阶段循环对它们进行排序,并在最后生成书面审计报告。
各阶段依次为 Measure → Diagnose → Fix → Verify,必须按顺序执行,不得跳过。阶段 1 使用真实设备上的 release + R8 构建版本建立基准数据,因为其他方式得出的数据都不可信。阶段 2 根据 Compose Compiler 报告、Layout Inspector 和运行时跟踪,将症状转化为明确的原因。阶段 3 一次只应用一项针对性修复,并在每次修复之间重新测量,以便验证每项改动带来的变化。阶段 4 重新生成 Baseline Profile,设置 CI 稳定性门槛,并提交基准文件,防止回归问题再次出现。
没有测量的性能优化就是猜测。Skydoves 的热门观点 #1 始终适用:不要追求 100% 可跳过性——它只是编译器报告中的一项诊断指标,并非目标。目标是在真实设备上改善 FrameTimingMetric 和 StartupTimingMetric。
何时使用此技能
- 开发者报告了笼统的卡顿问题,但没有指出具体界面(“应用感觉很沉重”“所有操作都很慢”)。
- 团队要启动一轮性能优化,希望制定结构化计划。
- 新团队成员需要了解存在性能问题的代码库。
- 发布前需要设置性能门槛。
- 用户要求进行“审计”“性能审查”或提供书面交付物。
- 用户不知道从哪里开始,希望 Claude 进行初步排查。
何时不使用此技能
- 已明确指出具体症状(
LazyColumn滚动卡顿、derivedStateOf未触发、自定义 modifier 发生重组)。请直接使用针对该问题的技能。请参阅../../INDEX.md中的症状→技能映射。 - 开发者只想修复一个问题,不需要书面报告。请从
../../INDEX.md中选择针对该问题的技能。 - 无法构建 release 版本(例如签名配置损坏)。请先解决该问题;此审计必须测量 release 版本。
前置条件
- 项目可以成功构建 release 变体(
./gradlew assembleRelease)。 - 至少有一台用于测量的 Android 实体设备。模拟器数据不具代表性。
- 已有 Macrobenchmark 模块,或愿意添加一个(请参阅
../../measurement/generating-baseline-profiles/SKILL.md)。 - 愿意将 Baseline Profile 和稳定性基准文件提交到代码库。
- 使用 Compose Compiler 1.5.5+,以支持
stabilityConfigurationFile。使用 Kotlin 2.0.20+,以启用默认的 Strong Skipping。使用 AGP 8.2+,以使用 Baseline Profile Generator 模板。 - 可选但建议:创建功能分支,将每项修复分别作为一个 PR 合并。
工作流
按顺序执行全部四个阶段。不要提前跳到后续阶段。每完成一项阶段 3 的修复,都要返回阶段 1 重新测量,并在应用下一项修复前回到阶段 2 重新诊断。
阶段 1 — 测量(在更改任何代码之前建立基线数据)
- 确认发布变体可以构建,并且是测量目标。交叉链接
../../measurement/testing-compose-in-release-mode/SKILL.md。 - 确认 R8 已正确启用(完整模式、
proguard-android-optimize.txt、启用资源缩减)。交叉链接../../build/configuring-r8-for-compose/SKILL.md。 - 通过 Baseline Profile Generator 模块生成或更新 Baseline Profile。交叉链接
../../measurement/generating-baseline-profiles/SKILL.md。 - 使用
MacrobenchmarkRule+StartupTimingMetric,在CompilationMode.Partial(BaselineProfileMode.Require)下采集冷启动数据。运行 ≥10 次迭代。 - 使用
FrameTimingMetric采集可疑界面的滚动数据(P50、P90、P99)。在同一设备上运行 ≥5 次迭代。 - 将每项数据记录在审计报告的“基线(阶段 1)”部分。任何代码更改之前必须完成此项。
// Phase 1 baseline scroll measurement (record P50/P90/P99 of frameDurationCpuMs)
@Test fun feedScroll() = rule.measureRepeated(
packageName = "com.example",
metrics = listOf(FrameTimingMetric()),
iterations = 5,
startupMode = StartupMode.WARM,
compilationMode = CompilationMode.Partial(BaselineProfileMode.Require),
) {
startActivityAndWait()
device.findObject(By.res("feed")).fling(Direction.DOWN)
}
阶段 2 — 诊断(将症状转化为明确原因)
- 为发布变体启用 Compose Compiler 报告。交叉链接
../../stability/diagnosing-compose-stability/SKILL.md。 - 阅读
<module>-composables.txt。列出每个可重启但不可跳过的可组合项,以及阻止跳过的不稳定参数。 - 阅读
<module>-classes.txt。列出每个不稳定类及其导致问题的字段(var、不稳定的字段类型、接口等)。 - 对于令人意外的判定(
runtime、unknown、“看起来稳定,但编译器不这么认为”),运行../../stability/understanding-stability-inference/SKILL.md,逐步梳理 12 阶段算法。 - 对可疑界面进行 Layout Inspector 检查:启用重组次数和跳过次数。找出次数高、跳过次数低的热点。交叉链接
../../recomposition/debugging-recompositions/SKILL.md。 - 如需进行发布级跟踪(Layout Inspector 无法检查发布构建),使用
@TraceRecomposition为主要热点添加埋点。交叉链接../../measurement/tracing-recompositions-at-runtime/SKILL.md。 - 排序问题:按频率 × 耗时排名。高频
LazyColumn行中的一次重组,优先级高于设置界面中一次性的 10 次重组。不要仅按编译器报告的严重程度排序。 - 撰写审计报告的“诊断”部分:前 5 个热点、可重启但不可跳过的可组合项数量、不稳定类数量、阶段错置读取数量。
Phase 3 — 修复(应用有针对性、最小差异的更改——每个 PR 解决一个原因)
针对每个已排序的问题,选择匹配的专项技能并应用修复。**首选:**每项技能单独提交一个 PR,以便审查和二分定位。每次修复后,重新运行 Phase 1(测量)和 Phase 2(诊断),确认更改达到了预期效果,且没有导致其他问题回退。
- 稳定性修复(不稳定的
data class、List/Set/Map参数、java.time.LocalDateTime)→../../stability/stabilizing-compose-types/SKILL.md。 - 强跳过审查(验证模式已启用,找出需要
@DontMemoize或@NonSkippableComposable的 lambda 捕获位置)→../../recomposition/using-strong-skipping-correctly/SKILL.md。 - 动画和滚动的阶段延迟修复(
Modifier.offset { }、Modifier.graphicsLayer { }、Modifier.drawBehind { })→../../recomposition/deferring-state-reads/SKILL.md。 -
derivedStateOf误用(缺少remember、捕获非状态变量,或在输入频率未高于输出频率时使用)→../../recomposition/choosing-derivedstateof/SKILL.md。 - 为
LazyColumn/LazyRow/LazyVerticalGrid设置惰性布局key和contentType,将修饰符链提升到itemslambda 外 →../../lists/optimizing-lazy-layouts/SKILL.md。 - 仅当 Compose Foundation 1.10+ 的默认设置在高速滚动时仍导致丢帧,才调整惰性预取 →
../../lists/configuring-lazy-prefetch/SKILL.md。 - 将自定义修饰符从
Modifier.composed { }迁移到Modifier.Node+ModifierNodeElement→../../modifiers/migrating-to-modifier-node/SKILL.md。 - 修复修饰符顺序错误(背景绘制区域错误、点击区域超出可见按钮、
background后使用clip)→../../modifiers/ordering-modifier-chains/SKILL.md。 - 确保 Flow 收集安全(
collectAsState→collectAsStateWithLifecycle,将Flow<T>参数提升到 composable 外,添加.conflate()/.distinctUntilChanged())→../../side-effects/collecting-flows-safely/SKILL.md。 - 审查 Effect API(
LaunchedEffect与RememberedEffect、DisposableEffect、SideEffect的选择,通过rememberUpdatedState处理过期回调)→../../side-effects/using-efficient-effects/SKILL.md。 - 每次修复后:重新运行 Phase 1 + Phase 2,并在审计报告的 Phase 3 表格中记录 Macrobenchmark 的变化。
Phase 4 — 验证(确保成果稳固)
- 重新生成 Baseline Profile,以捕获现在更快的代码路径。交叉链接
../../measurement/generating-baseline-profiles/SKILL.md。 - 将 Macrobenchmark 数值与 Phase 1 基线进行比较。在“验证(Phase 4)”部分记录 P50/P90/P99 的变化。
- 设置 CI 稳定性门禁,使回退导致构建失败(先运行一次
stabilityDump,之后在每个 PR 上运行stabilityCheck)。交叉链接../../stability/enforcing-stability-in-ci/SKILL.md。 - 提交
app/src/main/generated/baselineProfiles/baseline-prof.txt(或消费者模块放置该文件的位置)。 - 提交由
:stabilityDump生成的app/stability/*.stability基线文件。 - 编写审计报告(模板见下文)并分发。不得在没有书面报告的情况下宣布审计完成。
审计报告模板
Claude 必须在审计结束时生成此文件。将其保存到 docs/perf-audit-<date>-<module>.md(或项目存放报告的位置)。
# Compose Performance Audit — <date> — <module>
## Environment
- Compose UI: <version>
- Compose Compiler: <version>
- Kotlin: <version>
- AGP: <version>
- Device: <model> / <API>
## Baseline (Phase 1)
- Cold startup median: <ms>
- Scroll FrameTimingMetric (P50/P90/P99): <ms> / <ms> / <ms>
- Baseline Profile present: yes/no
## Diagnosis (Phase 2)
- Restartable-not-skippable composables: <count>
- Unstable classes: <count>
- Top 5 recomposition hotspots: <list>
- Phase-misplaced reads: <count>
## Fixes applied (Phase 3)
| Skill | Change | Files | Macrobench delta |
| ----- | ------ | ----- | ---------------- |
| stability/stabilizing-compose-types | wrap List<Snack> with ImmutableList | feed/SnackList.kt | scroll P90 18ms → 12ms |
| recomposition/deferring-state-reads | offset(x.dp) → offset { } | hero/Hero.kt | scroll P99 33ms → 19ms |
| ... | ... | ... | ... |
## Verification (Phase 4)
- Cold startup median: <ms> (Δ <ms>)
- Scroll FrameTimingMetric (P50/P90/P99): <ms> / <ms> / <ms> (Δ ...)
- Baseline Profile regenerated: yes
- CI stability gate active: yes / no
## Open items / follow-ups
- <list>
模式
模式:动手修改任何一行之前先测量
// WRONG
// "Scroll feels rough — let me wrap this List in ImmutableList and add @Immutable everywhere."
// WRONG because: no baseline number exists, so any later claim of improvement is unfalsifiable.
// RIGHT
// 1. Run MacrobenchmarkRule on the suspect surface. Record P50/P90/P99 in the report.
// 2. Read the Compose Compiler report for the same surface. Identify the named cause.
// 3. Apply ONE targeted fix from a sibling skill.
// 4. Re-run the same Macrobenchmark. Compute the delta. Record it.
模式:每个 PR 解决一个原因
WRONG: a single PR titled "perf improvements" that
- converts 4 data classes to ImmutableList parameters
- migrates 2 custom modifiers to Modifier.Node
- moves 3 graphicsLayer reads down a phase
- adds a Baseline Profile module
WRONG because: if the Macrobench delta is mixed (startup faster, scroll slower), the
audit cannot attribute the regression to a specific change. Bisect impossible.
RIGHT: four PRs, each scoped to a single skill, each with its own Before/After
Macrobench number in the description. The audit report links to each PR in the
Phase 3 table.
模式:可跳过性是诊断指标,而非 KPI
WRONG: "We got composables.txt skip rate from 71% to 100%. Audit complete."
WRONG because: skippability is a means. The end is FrameTimingMetric and
StartupTimingMetric improvement on a real device. A 100% skippable app can still
drop frames if the work happens in Layout or Draw.
RIGHT: "Scroll P90 dropped from 18ms to 11ms. Cold startup median dropped from
820ms to 610ms. Skip rate improved as a side effect; we did not target it directly."
Compose 版本要求附录
| 能力 | 最低版本 |
|---|---|
| Strong Skipping 默认开启 | Kotlin 2.0.20+ |
LazyLayoutCacheWindow | Compose Foundation 1.9+ |
| 懒加载预取中的可暂停组合(默认) | Compose Foundation 1.10+ |
stabilityConfigurationFile DSL | Compose Compiler 1.5.5+ |
| Baseline Profile Generator 模板 | AGP 8.2+ |
| R8 full mode 默认开启 | AGP 8.0+ |
Modifier.animateItem() 正式版 | Compose UI 1.7+ |
rememberGraphicsLayer() | Compose UI 1.7+ |
强制规则
- 必须按顺序完成全部四个阶段——测量 → 诊断 → 修复 → 验证。绝不可跳过阶段。
- 必须在更改代码前记录数据,否则无法验证任何改进声明。
- 必须让第 3 阶段的每项修复只针对一个明确命名的原因,并在修复之间重新测量,以证明每项更改带来的差异。
- 必须在第 4 阶段结束时提交重新生成的 Baseline Profile 和稳定性基线文件。
- 必须在 release + R8 环境下使用真实实体设备进行测量。Debug 数据和模拟器数据不具代表性。
- 不得在未根据上述模板生成书面报告的情况下宣布审计完成。
- 不得追求 100% 可跳过性(skydoves 热点观点 #1)——目标是改进
FrameTimingMetric和StartupTimingMetric,而不是优化编译器报告中的指标。 - 不得在未先阅读专项技能的前置条件和验证清单前,就应用该技能中的修复。
- **建议:**每项修复技能对应一个 PR——小型差异便于审查和二分定位。
- **建议:**第 2 阶段应按频率 × 成本对热点排序,而非按编译器报告中的严重程度排序。
验证
- 在更改任何代码前,已在报告中记录第 1 阶段数据(冷启动中位数;滚动时
FrameTimingMetric的 P50/P90/P99)。 - 已记录第 2 阶段诊断结果:可重启但不可跳过的数量、不稳定类数量,以及排名前 5 的热点。
- 报告中每项第 3 阶段修复都有单独一行,注明应用的具体技能、改动的文件,以及可测量的 Macrobenchmark 差异。
- 第 4 阶段:已重新生成并提交 Baseline Profile;已提交
app/stability/*.stability文件;CI 的stabilityCheck门禁已启用。 - 已保存并传阅审计报告(例如
docs/perf-audit-<date>-<module>.md)。 - 冷启动中位数和滚动 P90 均有所改善或保持稳定;若其中任一项退化,报告的“待处理事项”部分应说明原因和后续操作。
参考资料
同级技能(此编排器按序执行的 25 项专项技能)
第 1 阶段 — 测量:
../../measurement/testing-compose-in-release-mode/SKILL.md— Debug 构建的数据不可靠;应在 release + R8 环境下测量。../../measurement/generating-baseline-profiles/SKILL.md— 端到端使用 Baseline Profile Generator + Macrobenchmark。../../build/configuring-r8-for-compose/SKILL.md— 使用 full mode、优化 ProGuard 文件、信任使用方规则。 Phase 2 — 诊断:../../stability/diagnosing-compose-stability/SKILL.md— 启用并查看 Compose Compiler 报告。../../stability/understanding-stability-inference/SKILL.md— 12 阶段算法、$stable字段、泛型位掩码。../../stability/using-stability-analyzer-ide-plugin/SKILL.md— 用于行内稳定性检查的 IDE 插件。../../stability/visualizing-recomposition-cascades/SKILL.md— 可视化不稳定参数如何沿可组合树传播。../../recomposition/debugging-recompositions/SKILL.md— Layout Inspector 计数和参数变更原因。../../measurement/tracing-recompositions-at-runtime/SKILL.md— 使用@TraceRecomposition进行适用于发布版本的跟踪。
Phase 3 — 修复:
../../stability/stabilizing-compose-types/SKILL.md— 三层递进方案:重写、添加注解、配置。../../recomposition/using-strong-skipping-correctly/SKILL.md— 验证模式、审查 lambda 捕获位置、使用逃生方案。../../recomposition/deferring-state-reads/SKILL.md— 通过 lambda 修饰符将读取操作下推至 Layout/Draw 阶段。../../recomposition/choosing-derivedstateof/SKILL.md— 仅在输入频率高于输出频率时使用。../../recomposition/avoiding-subcomposition-pitfalls/SKILL.md— 判断何时SubcomposeLayout/BoxWithConstraints的成本高于收益。../../lists/optimizing-lazy-layouts/SKILL.md—key、contentType、Modifier.animateItem()、提升状态。../../lists/configuring-lazy-prefetch/SKILL.md—LazyLayoutCacheWindow、可暂停预取、NestedPrefetchScope。../../modifiers/migrating-to-modifier-node/SKILL.md— 用Modifier.Node+ModifierNodeElement替代composed { }。../../modifiers/ordering-modifier-chains/SKILL.md— “包装下一个修饰符”的思维模型。../../side-effects/collecting-flows-safely/SKILL.md— 使用collectAsStateWithLifecycle,提升Flow<T>参数。../../side-effects/using-efficient-effects/SKILL.md— 选择成本最低且正确的 effect API。
Phase 4 — 验证:
../../stability/enforcing-stability-in-ci/SKILL.md— 使用stabilityDump基线和stabilityCheckCI 门禁。../../measurement/generating-baseline-profiles/SKILL.md— 修复后重新生成;提交baseline-prof.txt。 热重载(提升开发迭代速度,可选但推荐):../../hot-reload/setting-up-compose-hotswan/SKILL.md— 安装并配置 HotSwan,实现亚秒级迭代。../../hot-reload/preserving-state-across-reloads/SKILL.md— 在热重载后保留remember/rememberSaveable状态。../../hot-reload/understanding-hot-reload-limits/SKILL.md— 了解哪些内容可以顺利热重载,哪些情况会导致完整重建。../../hot-reload/iterating-with-ai-and-mcp/SKILL.md— 将 HotSwan 与支持 MCP 的 AI 工具配合使用,实现快速迭代。
按症状和 API 查找:../../INDEX.md。
外部参考资料
- Android Developers — 性能概览:https://developer.android.com/develop/ui/compose/performance
- Android Developers — 稳定性概览:https://developer.android.com/develop/ui/compose/performance/stability
- Android Developers — 阶段与性能:https://developer.android.com/develop/ui/compose/performance/phases
- Android Developers — 基线配置文件:https://developer.android.com/develop/ui/compose/performance/baseline-profiles
- Ben Trengrove — 为什么要在发布版本中测试性能:https://medium.com/androiddevelopers/why-should-you-always-test-compose-performance-in-release-4168dd0f2c71
- Skydoves — “优化应用性能的 6 条 Jetpack Compose 指南”:https://medium.com/proandroiddev/6-jetpack-compose-guidelines-to-optimize-your-app-performance-be18533721f9
- Skydoves — “通过掌握稳定性来优化应用性能”:https://medium.com/proandroiddev/optimize-app-performance-by-mastering-stability-in-jetpack-compose-69f40a8c785d
- Skydoves — compose-performance 资源中心:https://github.com/skydoves/compose-performance
- Chris Banes — 可组合项指标:https://chrisbanes.me/posts/composable-metrics/