Engineering practice
Performance
What has been measured, on what, and the numbers worth comparing against.
# The hardware that matters
The weakest device we run on sets the bar: a Google TV box on Android 14 with 1.84 GB of RAM and a 32-bit ARM ABI, rendering at 1080p.
No phone performance figure transfers to it. Nor does an emulator's: a television emulator is x86_64 with desktop memory. It is good for proving a screen composes and focus moves, and worthless for anything timed.
# What has been measured
Against a synthetic 20,000-entry playlist served over a loopback tunnel, on a mid-range tablet, in a minified release build:
| Measure | Result |
|---|---|
| Ingest 20,002 entries | ~4 s end to end |
| Cold start, populated database | Median 445 ms against a 2000 ms budget |
| Cold start on Android 11 | Median 328 ms |
| Scroll, six hard flings | 5.93% janky; 90th percentile 28 ms, 95th 36 ms |
Loading twenty thousand channels costs nothing at startup, which matches the architecture: Koin starts, Room is built lazily, and nothing queries the database before the first frame.
Two earlier readings looked alarming and were both artefacts, recorded so nobody re-derives them: the first launch after install took 1590 ms — one-time dexopt and ART warmup — and a debug build took ~1850 ms, which is unminified-build overhead rather than data volume.
# What is now stale
Those figures predate the indexing and threading fixes in the browse path. They need re-running, and the direction of the change is not obvious:
- Every emission after the first is cheaper — no table scan, no temporary sort, no main-thread mapping.
- The first launch after upgrading is more expensive, because schema 10 builds two indices over the whole playlist, and schema 11 rebuilds two tables. Neither cost is in the old numbers.
- Every browse query now also filters by profile. It is an indexed equality on a key column, so the expectation is no measurable change — but it is an expectation, not a reading.
The jank baseline is the number to compare against after any change to a browse list.
# Measuring playback, rather than discussing it
The player reports time to first frame and a rebuffer count, and the second is the one that matters for smoothness. It deliberately counts only buffering after the first frame, because buffering before it is startup and a measure that mixes the two cannot show an improvement in either.
These exist because "it feels smoother" is not evidence, and because the VOD stutter was a fault that recovered every time — it produced no error, no crash, and nothing in a log. The only way such a thing shows up in a sweep is if something is counting.
# How to measure
Cold start: force-stop, launch, read TotalTime, repeat six times,
report the median — never the first run after an install.
Jank: reset gfxinfo, scroll deliberately, read the percentiles back. On a
television, "scroll" means holding the D-pad, which moves faster than any finger.
Requests to a provider: count them at the source, not by eye. The whole class of bug that got our account blocked is invisible from the UI.