3 Commits

Author SHA1 Message Date
SoftwareApp 4846454645 Ship a baseline profile, generated on the emulator because the phone cannot collect one
The profile is real: 13193 lines in app/src/main/baseline-prof.txt, 584 of them the app's own classes, bundled into the release APK as assets/dexopt/baseline.prof. It is produced by a new :baselineprofile test module that walks the first journey - roots screen, one volume opened - under the androidx.baselineprofile plugin, and :app now depends on androidx.profileinstaller so the bundled profile is applied at install time.

Two device facts decided how it was made. Collection requires API 33+ (BaselineProfileRule refuses Android 12 outright), so it ran on the API 35 AVD that was already in ~/.android/avd; that emulator is software-rendered and needs androidx.benchmark.suppressErrors=EMULATOR for any macrobenchmark. And the release build needed profileable android:shell=true, which is why app/src/release/AndroidManifest.xml exists and says why: the same APK is both the thing measured and the thing shipped, so the profile's class names are the ones in the shipped dex.

The effect is not measured, and PERFORMANCE.md says so: 7404 ms median without the profile against 9005 ms with it, run to run, because a TCG emulator varies by more than a profile can buy. What remains is one run on a physical API 33+ phone, with the two compile commands documented. The emulator did settle the other open question - FrameTimingMetric there reports frameDurationCpuMs and frameOverrunMs, the metrics this phone cannot produce at all.

:baselineprofile is compiled by tools/verify.sh --release like :benchmark, so it cannot rot between device runs.
2026-10-17 17:06:08 +02:00
SoftwareApp 1f94d0a38a Add a macrobenchmark module, and find out what the build was hiding
docs/PERFORMANCE.md has been honest that three of its four numbers do not exist. They exist now as
measurements, if not yet as results: benchmark/ is a macrobenchmark module with four tests - cold start
with StartupTimingMetric, then opening, scrolling and searching a folder of a thousand files with
FrameTimingMetric - sharing a fixture at /sdcard/AABenchmark that is created once per run and deleted in
@AfterClass. Frames rather than a named trace section on purpose: what a user calls slow is jank on
screen, and the alternative puts benchmarking calls into production code for the sake of a number.
CompilationMode.None() is the honest cold-install baseline; Partial() plus a baseline profile is the
follow-up and needs the same module. The storage grant is applied with appops in setup, which is what
lets the run be unattended - a permission dialog is the one thing a test cannot answer.

Three build facts had to be discovered rather than designed, and each is now written where it will be
read. The app under test cannot keep its own package id: it needs a debug signature to install on a test
device, so it cannot replace a release install, and an un-suffixed build is exactly that. It also cannot
use the obvious suffix: `.benchmark` is already the benchmark module's own APK id, and the test APK then
fails to install with INSTALL_FAILED_VERSION_DOWNGRADE - which is what the first attempt did. Hence
`.perf`. And `testBuildType = "benchmark"` cannot be set unconditionally, because it makes benchmark the
variant that *unit* tests run against and :app:testDebugUnitTest - the task the entire gate is built on -
stops existing; it is behind -PbenchmarkBuild instead. All three were measured, not guessed, and the
third would have broken CI silently.

Two smaller things came with it. tools/verify.sh --release now also compiles the app's benchmark variant
and the test APK, because build wiring nobody runs is wiring that rots; no device is involved. And the
code map grew an EXTRA_ROOTS section so it indexes benchmark/ too - a map that silently omits a module
lies by omission, which is worse than the one line it costs.

The numbers themselves are still missing, and for a device reason rather than a code one: installing the
new package id on this MIUI phone fails with INSTALL_FAILED_USER_RESTRICTED (the "Install via USB" gate,
which wants a human and answered too late). Both APKs are pushed to /sdcard/Download/ for a manual
install, and the run is one command away:
`adb shell am instrument -w -e class de.softwareapp_hb.filemanager.benchmark.BrowserBenchmark de.softwareapp_hb.filemanager.benchmark/androidx.test.runner.AndroidJUnitRunner`.

Gate green with --release, including the new compile step; map current; 386 tests unchanged.
2026-10-17 09:45:36 +02:00
SoftwareApp adfd95fc38 M0: project scaffold (Gradle 9.3.1 / AGP 9.1.0, Compose shell, storage permissions) 2026-10-14 14:55:10 +02:00