Files
ai-app-bench/README.md
T
irisandClaude Fable 5.1 8eb87a2289 iris: rebuild with the phone-crash diagnostic and without force-gles
The bench APK aborted on Iris's phone in AndroidRenderer::new with only
a truncated "wgpu error: Validation Error". This build carries ai-app-2
commit 46246ea: a renderer-creation failure now reports the adapter,
its limits/downlevel flags, and wgpu's full error chain on screen and
in logcat instead of aborting, and build-apk.sh no longer forces the
GLES backend (force-gles was meant only for one emulator measurement,
but the delivered APK inherited it regardless of target -- the named
hypothesis for the crash, per ai-app-2's RUST.md P0 box).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 23:05:27 -04:00

8.1 KiB

ai-app bench

The two APKs for the phone benchmark gate (ai-app's docs/RUST.md, P0), packaged for Dev Updater so they can be installed on a phone without a build machine. Add this checkout as a project in Dev Updater (its .dev-updater.ron names two APK components, compose-bench and iris-bench, with no build step -- the APKs are committed under compose/ and iris/), then install each from its card. Both install beside the real app under their own application ids and need no server, no enrolment and no permissions.

The rest of this file is the runbook that was delivered with the APKs.


P0 benchmark APKs

Two arm64 builds, asked for in docs/RUST.md's P0 box (the phone benchmark gate Iris asked for 2026-09-05, "before P1 I'd like to see benchmarks & also maybe stress test on my own phone"). Each opens straight onto the same synthetic fixture transcript with no server, runs the same scripted scroll-and-stream benchmark, and produces a text report to paste back. Install both, run both, paste both reports back -- the gate is a comparison, not either number alone.

Compose (compose-bench-arm64.apk)

  1. Install it (it has its own application id, com.example.aiapp.bench, and its own label "AI Sessions bench" -- it installs beside a real enrolled "AI Sessions" without touching it).
  2. Open it. It goes straight to a session screen holding the fixture transcript -- no enrollment, no permission prompts.
  3. Tap the gear icon top-right ("Session settings").
  4. Tap "Run benchmark". It scrolls the transcript (the same 24-swipe loop app/transcript-bench.sh drives on a phone from a computer), then streams 400 more fixture events in over 20 seconds pinned to the newest end (the same shape app/stream-bench.sh measures), then copies a report to the clipboard and shows a toast.
  5. Paste the clipboard back here (or in whatever you're pasting reports into). It is plain text with units on every number: frame counts and percentiles, the draw-phase accounting, and a bench: section this build added -- process CPU time, peak RSS, and battery current sampled once a second (says "unavailable" rather than a fabricated number where the phone can't answer, per this project's rule against showing an inferred value as a measured one).
  6. If you want to run it again, "Run benchmark" can be pressed more than once -- each press resets the counters first, so the report only covers that run's own scroll and stream.

Built from app/androidApp's bench build type; see app/build-apk.sh bench and app/bench-fixture/README.md for how the fixture it opens was generated (deterministic, checked into the repo, never a real transcript).

iris (iris-bench-arm64.apk)

  1. Install it -- own application id, dev.iris.android.demo.bench (an applicationIdSuffix on the same dev.iris.android.demo the plain iris tabs demo uses, so it installs beside that one too), needs no permissions beyond INTERNET (unused here -- inherited from the same manifest transcript-screen needs, harmless for a build that talks to nothing).
  2. Open it. Same fixture as the Compose build (the identical checked-in app/bench-fixture/assets/transcript.jsonl), opened with no server through client-core's real fold_page -- there is no enrollment step here at all, iris's shell has none yet.
  3. Tap "Run benchmark" (the whole screen is just two buttons above the transcript and a report area below it -- no settings dialog to find it in). It runs the identical scripted loop: the same 24-swipe/6-cycle scroll (900px over 200ms, 500ms apart, animated in ~60Hz steps rather than jumped so real frames render along the way), then the same 400-event/20s streaming phase pinned to the newest end, through fold_event -- the same fold path a live SSE reply uses.
  4. The report appears in the text area under the buttons (it is a selectable field, so it can be copied by hand too) and is logged under logcat's iris-android-app tag on the line starting iris bench report:. Tap "Copy report" to put it on the clipboard through the shell's own ClipboardManager (no toast -- watch logcat or the on-screen text for confirmation it worked).
  5. Paste it back. Same units, most lines the same shape as Compose's -- frame count, janky percentage, p50/p90/p99/worst -- plus iris's own CPU/GPU split (cpu_p50/gpu_wait_p50, from FrameReport::record_split) where Compose's report has its draw-phase accounting instead, and a bench:-shaped tail with the same three added fields: process CPU time over the run (getrusage(RUSAGE_SELF)), peak RSS (/proc/self/status's VmHWM), and battery current sampled once a second through BatteryManager.getIntProperty(BATTERY_PROPERTY_CURRENT_NOW) -- "no frames recorded" / "unavailable" rather than a fabricated number wherever the phone can't answer one, same rule Compose's report follows.
  6. "Run benchmark" can be pressed again; it resets FrameReport first, so a second run's report covers only that run.

Built with iris/android-app/build-apk.sh release --abi arm64-v8a (wraps cargo ndk -t arm64-v8a -P 26 -o app/src/main/jniLibs/ build --release --features "transcript-screen bench" plus gradle :app:assembleRelease, signed with the same ~/.config/ai-app/release.jks app/build-apk.sh generates, and verifies the result with aapt2/apksigner). No longer built with force-gles as of the 2026-09-06 rebuild below -- see that entry for why it was there and why it had to come out for a phone build specifically.

2026-09-06, commit 46d3a6f: rebuilt after transcript_ui::TranscriptScreen::apply replaced the full-rebuild-per-event streaming path in all three iris clients (docs/RUST.md's P0 box has the before/after numbers) -- this is the build to use if comparing against an iris APK from before that commit.

2026-09-06, second rebuild, ai-app-2 commit 46246ea: this build aborted on Iris's phone (a Pixel, GrapheneOS) in AndroidRenderer::new, on the very first surface_changed, with only wgpu error: Validation Error surviving into the crash report before Android truncated it. Two things changed:

  1. A renderer-creation failure no longer aborts the process. iris_core::UiRenderNode::new now runs its bind-group-layout/pipeline creation inside wgpu error scopes and returns Result<Self, String> instead of letting wgpu's default handler panic; the Android backend turns a failure into the adapter's identity, the exact limits and downlevel flags bind-group layouts validate against, and wgpu's own "Caused by" chain, logged as one line under the iris-android-app logcat tag and shown on screen as plain, selectable, scrollable text (a TextView swapped in for the whole activity) saying to copy it and send it back. If this build still fails on the phone, that screen is what to screenshot or copy -- there is no more silent abort to chase through a truncated crash report.
  2. This build no longer forces the GLES backend. Every earlier iris-bench-arm64.apk (including the one above) was built with --features "... force-gles ...", a flag that exists only to force the emulator off its default software Vulkan and onto GLES for one specific frame-time measurement (RUST.md's I5) -- its own doc in iris/Cargo.toml never mentions real hardware. build-apk.sh's default feature list carried it into every arm64 build regardless, so the APK actually delivered to a phone was locked to GLES rather than the phone's own Vulkan driver. That is the named hypothesis for the crash (docs/RUST.md's P0 box has the full audit): GLES support for a storage buffer bound in the vertex stage (move_offsets, masks_layout) depends on the driver reporting GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS > 0, which real Vulkan grants unconditionally but a phone's GLES path is not guaranteed to -- and RUST.md's own SwiftShader finding already flagged GLES-on-Android as the fragile backend for this exact shader. This build uses the default backend (Vulkan on a real device) instead.

If this rebuild still crashes, the on-screen report from point 1 names the real cause directly; paste it back rather than guessing further from a truncated log.