agentleFS
SkillMedevSkillMedev/mobile-app-engineering/skills/mobile-perf-profiler/SKILL.md

mobile-perf-profiler

Skill0 starsChanged 39 days agoMITView on GitHub

Diagnoses and fixes mobile jank, dropped frames, slow startup, and growing memory by capturing a trace or heap snapshot on a real device, isolating the single worst cost, fixing it, and re-measuring against the 16ms frame and cold-start budgets. Use when someone says "the app stutters when I scroll", "startup takes forever", "memory keeps climbing after navigating around", or the UI feels laggy on a real device. Do NOT use when the bottleneck is a website or web app (LCP, bundle size, slow endpoint or query) - use web-performance instead; for Compose recomposition fixes pair with jetpack-compose-builder; for Flutter widget architecture use flutter-widget-architect.

The file

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 8 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every claim traces to a capture: no fix without the trace or snapshot line that motivated it, no "done" without the re-measurement.

    line 80
  2. Distinctive:

    Do NOT prescribe a fix without a captured trace or heap snapshot - no folklore optimizations.

    line 87
  3. Distinctive:

    Do NOT profile a debug build, a simulator/emulator, or a flagship device and treat the numbers as real.

    line 88
  4. Distinctive:

    Do NOT skip the cheap rungs: field vitals and overlays localize the symptom before a single trace is captured.

    line 89
  5. Distinctive:

    Do NOT micro-optimize a path the profiler shows is cheap.

    line 90
  6. Distinctive:

    Do NOT chase averages or synthetic benchmarks instead of the frames that actually miss the budget.

    line 91
  7. Distinctive:

    Do NOT load the full dataset to render a screenful of rows.

    line 92
  8. Distinctive:

    Do NOT fix web-side latency here - a slow endpoint, query, or page render is web-performance territory.

    line 93

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Mobile Perf Profiler

Diagnose mobile performance from measured evidence, fix the largest cost, and re-measure - never guess. The costly failure is folklore optimization: weeks spent caching and micro-tuning paths the profiler would show are cheap, while the one 40ms bind on the scroll path ships untouched.

Operating procedure

Step 1: Gather inputs

  1. The exact symptom, named precisely: jank while scrolling a specific list, jank on first frame of a screen, slow cold start, or memory that climbs. "The app feels slow" is not a symptom yet - reproduce until it is one.
  2. A representative device: a real mid-tier phone from the install base, not the developer's flagship and never a simulator/emulator.
  3. A release or profile build. Debug builds misreport timing by integer factors.
  4. The display refresh rate of target devices - 60Hz means a 16.6ms frame budget, 120Hz means 8.3ms.
  5. Field data access: Play Console Android vitals, Xcode Organizer / MetricKit. If available, this decides which symptom is worth the trace before any local work.

Step 2: Work the evidence ladder, cheapest first

Escalate only when the cheaper rung has not localized the cause:

  1. Field data (minutes, zero setup): Android vitals and Xcode Organizer already know the slow-frame rate, frozen-frame rate, cold-start percentiles, and which devices are worst. Confirm the reported symptom exists at scale and pick the worst screen/device class.
  2. On-device overlays (seconds to enable): Android GPU rendering profile bars, Flutter performance overlay. Watch the bars while reproducing - this tells which screen and roughly which frames blow the budget.
  3. Trace capture (minutes per run): iOS Instruments - Time Profiler (CPU), Animation Hitches (rendering); Android Perfetto/system tracing; Flutter DevTools timeline reading the UI-thread vs raster-thread split. This names the expensive function or phase.
  4. Heap snapshot diff (minutes per run): iOS Allocations and Leaks; Android Studio Memory Profiler and LeakCanary. Only for memory symptoms.
  5. Repeatable benchmarks (hours to wire): Android Macrobenchmark, XCTest performance metrics. Reserve for regression-gating a fix in CI, not for initial diagnosis.

Step 3: Locate the single worst offender in the trace

Find the frames that miss the budget and the single most expensive thing they do. For Flutter, read which thread is blown first: raster-thread jank is overdraw or shader compilation; UI-thread jank is Dart build/layout. For memory, diff a heap snapshot before and after the repro loop. Do not change code until the evidence points at one cause.

Step 4: Apply the fix that matches the measured cause

  • Over-rendering: stop rebuilding/recomposing subtrees that did not change (in Compose, hand recomposition fixes to jetpack-compose-builder); move allocation, parsing, and I/O off the build/measure/onDraw path; cache derived values; flatten the view hierarchy; reduce overdraw from stacked opaque layers.
  • Long lists: virtualize with cell reuse (RecyclerView, LazyColumn, UICollectionView, ListView.builder) so only visible rows exist; give stable keys/ids and fixed or estimated item sizes; paginate the data source.
  • Slow startup: defer non-critical init off the critical path (lazy dependency graphs, background warmup after first frame); trim Application/AppDelegate work; warm or simplify shader compilation and cold layout inflation for first-frame jank.
  • Leaks: diff before/after heap snapshots for objects retained that should be gone - undisposed controllers/streams/subscriptions, listeners never removed, static or singleton references to a destroyed Activity/ViewController, unbounded image caches. Decode images at display size, not source size.

Step 5: Re-measure on the same device and build

Confirm the symptom is gone against the budget on the same device, build type, and repro steps - not against a microbenchmark. If the fix matters, pin it with a Macrobenchmark/XCTest metric in CI so it cannot silently regress.

Budgets and red lines

  • Frame budget: 16.6ms per frame at 60Hz, 8.3ms at 120Hz. Jank is a single missed deadline the user sees, not an average.
  • Excessive slow frames (Android vitals red line): more than 50 percent of frames over 16ms. Excessive frozen frames: more than 0.1 percent of frames over 700ms.
  • Cold start: Android vitals flags cold starts at or over 5s, warm at or over 2s, hot at or over 1.5s. Target cold start under 2s on a mid-tier device; a cold start over 5s is a launch blocker, not a nice-to-have.
  • Memory: flat under repeated navigation (push a screen, pop it, 10+ times). Any monotonic growth across that loop is a leak, regardless of absolute size.

Worked artifact: trace-reading decision example

Symptom: fling on the order-history list drops frames on a mid-tier Android device.

Evidence ladder:
1. Vitals: order screen worst; slow-frame rate 31% on this device class.
2. GPU bars: green (input/draw) fine; long blue/purple segments during fling
   -> frame time spent in UI-thread work per row, not GPU.
3. Perfetto trace, worst frame = 41ms:
   RecyclerView onBindViewHolder          38.2ms
     SimpleDateFormat.<init> + format     22.1ms   <- allocation + parse per bind
     Glide decode (full-size bitmap)      11.4ms   <- decoding at source size
     remaining bind work                   4.7ms
Verdict: fix date formatting first (54% of the frame), image decode second.
Fix: preformat dates in the data layer; request display-size decode.
Re-measure, same device/build: worst frame 9.8ms; vitals slow-frame rate 4%.

The order of fixes follows the measured cost split, not intuition - image loading "feels" like the culprit, but the trace says dates.

Deliverable

Produce a performance case file: the named symptom and repro steps, device and build used, the evidence at each ladder rung climbed, the trace or snapshot excerpt naming the worst cost with its milliseconds, the single fix applied, and the before/after measurement against the budget - plus the CI benchmark pinning the win where one was wired.

Quality bar

  • Every claim traces to a capture: no fix without the trace or snapshot line that motivated it, no "done" without the re-measurement.
  • Measurements come from a release/profile build on a real representative device.
  • Success is every frame holding the 16.6ms (or 8.3ms) budget on that device, cold start under the vitals thresholds, and memory flat across a 10x navigation loop.
  • Fixes were applied in measured-cost order, largest first, one at a time so the re-measurement attributes the win.

Do NOT

  • Do NOT prescribe a fix without a captured trace or heap snapshot - no folklore optimizations.
  • Do NOT profile a debug build, a simulator/emulator, or a flagship device and treat the numbers as real.
  • Do NOT skip the cheap rungs: field vitals and overlays localize the symptom before a single trace is captured.
  • Do NOT micro-optimize a path the profiler shows is cheap; readability beats a nanosecond no user perceives.
  • Do NOT chase averages or synthetic benchmarks instead of the frames that actually miss the budget.
  • Do NOT load the full dataset to render a screenful of rows.
  • Do NOT fix web-side latency here - a slow endpoint, query, or page render is web-performance territory; the mobile trace will show the wait as idle network time, which is the handoff signal.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.