mcpField
A
auditing-compose-performance

auditing-compose-performance

github.com/skydoves测试与质量520145 次安装19 周前更新
代码仓库

关于 auditing-compose-performance

面向 Android 开发者的 Compose 性能审计技能,按测量、诊断、修复、验证四阶段组织工作,并要求产出书面审计报告。

主要功能

  1. 01四阶段审计流程
  2. 02基于真实设备测量
  3. 03借助编译器与运行时诊断
  4. 04逐项应用定向修复
  5. 05生成基线配置并设 CI 门禁
  6. 06记录审计报告

适用场景

  1. 01排查应用整体卡顿
  2. 02规划性能冲刺
  3. 03为发布设置性能门禁
  4. 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,将修饰符链提升到 items lambda 外 → ../../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+
LazyLayoutCacheWindowCompose Foundation 1.9+
懒加载预取中的可暂停组合(默认)Compose Foundation 1.10+
stabilityConfigurationFile DSLCompose 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 阶段 — 测量:

Phase 3 — 修复:

Phase 4 — 验证:

按症状和 API 查找:../../INDEX.md。

外部参考资料

评论

支持 Markdown