Add a jank scenario and three jank plants, captured - #392
Merged
Merged
Conversation
A tap anywhere on the feed pauses the row being watched and a second tap plays it again, which is what a short-form feed's viewer expects of the picture in front of them. The whole list is the target, not the row's box: the watched row is the one at the top and a thumb lands in the middle. The pause belongs to the row, so a row scrolled back to is still paused on whichever player it gets. It is also the feed's first click handler. devicelab's jank scenario taps the middle of the feed, a point that is on the list on any phone, and perfettoagent#6's click-handler regression needs a real handler to land in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017etd1nNb2E2XPL6WTvkAbo
`lab run jank` opens the feed, runs its plan once untraced as a warm-up and scrolls back to the top, then traces the same plan: three rounds of eight swipes down the feed, each ending in two taps that pause and play the watched row. The trace carries the frame timeline, the demo's `view` and `dalvik` slices and the scheduler, and not SurfaceFlinger's `gfx`, which nothing here reads (perfetto/sources/jank.pbtxt). The report reads the trace with five queries in jank/sql: the window's frames (the video `SurfaceView` layers left out), the app's work per frame, the main thread's states, where it slept in the middle of work by the slice it slept in, and GC. The harness gains `scenario_setup`, run once playback is confirmed and before a `playing` trace starts, so that getting to the feed is not in the trace, and `ui_tap_centre`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017etd1nNb2E2XPL6WTvkAbo
- `feed-tap-sleep`: the feed's tap handler holds the main thread for 120 ms. - `feed-grain-allocations`: a grain over the feed, drawn from a million boxed floats allocated in each scrolled frame's draw. - `feed-row-remeasure`: every row's padding breathes, so each visible row is re-measured every frame, and a row's re-measure fits its title by stepping its font size. Each is sized from pilot captures on the API 36 emulator; plants/README.md has the sizes and the reasons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017etd1nNb2E2XPL6WTvkAbo
…ries that say what they count - The feed takes its tap through a tap gesture and a semantics action rather than `clickable`, which merged every row into the list's one accessibility node. - `open_feed` and `wait_session_state` move to lib/playback.sh; the leak hunt and the jank scenario open the feed the same way, with the service's player confirmed paused first. - The jank scenario refuses a knob that is not a count, checks the warm-up left the feed at its top, and says what `--trace-from launch` adds. - gc.sql reads collections on `HeapTaskDaemon` only and lists every wait for one by thread; frames.sql leaves out frames with no present; blocked.sql says how it differs from `sleeping_in_slice_ms`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017etd1nNb2E2XPL6WTvkAbo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017etd1nNb2E2XPL6WTvkAbo
…pilots Fifteen runs on the API 36 emulator, all from 57e3144 with a clean tree, in three cycles of clean, the three plants, and clean again. By hand: - feed-tap-sleep: six sleeps of 120–123 ms inside `AndroidOwner:onTouch` in every planted run, none over 16 ms in any clean one. - feed-grain-allocations: 134–149 collections taking 9.5–11.0 s against 6–8 taking under half a second, a main thread that waits for GC only when planted, and app jank 64–67% against 37–55%. - feed-row-remeasure: frame p50 62–63 ms against 33–40 and p95 84–93 against 68–82, above every clean run but by less than the clean spread. The clean pairs are B1/C1, B2/C2 and B3/C3. plants/README.md sizes each plant from pilot runs: 120 ms stands; the grain is a million objects in each scrolled frame's draw, not 50k, because smaller or elsewhere it moved GC and no frame; and the re-measured row fits a title, because re-measuring the demo's rows alone moved the middle of the frames and not the tail. The harness README, the command list and the docs index name the scenario. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017etd1nNb2E2XPL6WTvkAbo
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes ramesh130/perfettoagent#6
A
jankscenario for devicelab, three plants for it, and fifteen captures.devicelab/lab run jank [--plant <name>]opens the feed, runs its plan once without tracing as a warm-up, and checks it scrolled back to the top. It then traces the same plan: 3 rounds of 8 swipes, each round ending in two taps (pause, then play the watched row). Each trace is about 55 s and 29–32 MB.Decisions
ui_tap_centre, a screen fraction like the swipes). It is a tap gesture plus a semantics action, notclickable, which would have merged every row into one accessibility node.scenario_setuphook: it gets the demo to the feed and warms it up before aplayingtrace starts.open_feedandwait_session_statemove tolib/playback.sh, shared with the leak hunt.gfx(perfetto/sources/jank.pbtxt:view,dalvik,binder_driver, sched).android.frames.timelinestill finds every frame.devicelab/plants/README.mdhas the tables):feed-tap-sleep: 120 ms, as the roadmap says.feed-grain-allocations: 1,000,000 boxed floats in each scrolled frame's draw, not 50k. At 50k, GC moved but no frame metric did. Work done between frames showed up only as fewer frames.feed-row-remeasure: the padding breathes by 4 dp, so every visible row is re-measured every frame (ADR-0007). A re-measure of these small rows moved p50 but not p95, so each row's re-measure also fits its title in 0.05 sp steps.Captures
All captures are on
superplayer_verify_36(API 36), from base57e3144, clean tree. Runs are indevicelab/out/<run>-jank/of the main checkout; pilots are indevicelab/out/jank-pilots/. Plant patch sha256s: sleep27700ab54513…, grainba6902ce089a…, remeasure1a29bb777a35….doFramep95onTouch20260924T033342Z(B1)20260924T033648Z(S1)feed-tap-sleep20260924T033945Z(G1)feed-grain-allocations20260924T034233Z(R1)feed-row-remeasure20260924T034924Z(C1)20260924T035208Z(B2)20260924T035447Z(S2)feed-tap-sleep20260924T035727Z(G2)feed-grain-allocations20260924T040009Z(R2)feed-row-remeasure20260924T040632Z(C2)20260924T040915Z(B3)20260924T041151Z(S3)feed-tap-sleep20260924T041431Z(G3)feed-grain-allocations20260924T041712Z(R3)feed-row-remeasure20260924T042341Z(C3)By hand:
AndroidOwner:onTouchin every planted run, 724–726 ms in total. No clean run has one over 16 ms.GC: Wait For Completion Alloc) only in planted runs.doFramep95 is 60–67 ms against 32–39. App jank is 64–67% against 37–55%; that gap is 10 points, less than the widest clean pair.Frames stdlib evidence (for perfettoagent#8)
Checked with perfettoagent's pinned trace processor on API 36 without
gfx:android_framesfinds the window's frames plus the few that never reached the timeline: 701 = 676 + 25 in B1. Itsdurp95 matches the window layer's (81.2 vs 81.5 ms).actual_frame_timeline_idusually points at a videoSurfaceViewlayer's row (563 of 701). App jank read through that join is 12.7%, against 54.7% from the window layer. Add MediaRequest with content identity, ordered sources, and start position #8 must filter by layer.jank_type != 'None'is saturated at 92–100% on the emulator (Buffer Stuffing, Prediction Error). The usable signal isApp Deadline Missed.Review (two axes)
775b0a0and the docs commit:clickableaccessibility merge, the pause race (the service player is now confirmed paused), duplicated feed-opening code, unexplained constants, the SQL headers and the GC scope (collections on HeapTaskDaemon, waits by thread), the tap-coordinates paragraph in the harness README, the docs index, the CLAUDE.md commands, and knob validation plus tests.ui.sh.57e3144is recorded inrun.json, and its main equivalent will be noted on the issue.Checks
devicelab/test/selftest: devicelab 104, jank 20, leak-hunt 34, startup 16 passed../gradlew assemble check --max-workers=1: BUILD SUCCESSFUL, 1187 JUnit tests, 0 failures.assembleDebug lintDebug spotlessCheck: green.Skipped
Compose:onForgotten, 2–3 s per run); it is noted against Measure UI jank during playback in the feed demo #51 indevicelab/jank/README.md.run.json(every capture used the defaults).🤖 Generated with Claude Code
https://claude.ai/code/session_017etd1nNb2E2XPL6WTvkAbo