Take the two main-thread disk reads off the startup path, and report what that bought

Both costs StrictMode found on a cold start are now off the main thread. scratchRoot is a Lazy<File>, so Context.getCacheDir - which checks and creates the private cache directory, measured at 492 ms - runs when a preview first copies something instead of while the view model is being created. The free-space probes moved into fillRootFreeSpace and fillFreeSpace, which run on Dispatchers.IO after the rows are already on screen and update the state when the filesystem answers; nothing waits for them, and fillFreeSpace checks the path is still the one on screen so a fast double-tap cannot leave a folder showing another one's number.

Measured after: StrictMode no longer reports either violation, and the only ones left are MIUI's own font hook - FontSettings.loadFontSetting, TypefaceUtils.loadFontSettings, InterceptorProxy.create via MainActivity.onCreate, in 50-78 ms pairs. The debug cold start went from a 1814 ms median to 1803 ms and the benchmark coldStart from 519.5/531.6 ms to 485.6/568.9/600.0 ms. That is no improvement, and PERFORMANCE.md says so in those words: both violations were first-call costs (cache directory creation once per install, a cold StatFs page cache), the timed runs are warm, and the device's run-to-run spread of 115 ms is wider than anything the change could have bought. What it does change is the launch right after an install, which is the benchmark's first iteration: 648 ms max fresh against 550 ms warm.
This commit is contained in:
2026-10-17 16:11:28 +02:00
parent cbb36abfce
commit 14615f9842
4 changed files with 91 additions and 12 deletions
+29 -4
View File
@@ -46,10 +46,35 @@ The same debug build now logs StrictMode violations, and the first cold start pr
| 477 ms | `FileManagerTheme` → `typography = FileManagerTypography` | no — the platform loading its own typefaces on first composition |
| 54 ms × 2 | `miui.util.font.FontSettings` via `MainActivity.onCreate` | no — the OEM's font hook doing `File.exists` while the activity is created |
That is **~1.5 s of main-thread disk I/O inside a 1.8 s start**, of which **~0.9 s is ours**. Both of
ours are mechanical to move and neither is fixed yet: the cache directory is only needed when a preview
copies something, and the free-space line on the roots screen can arrive a frame later. They are the
first items of the speed work.
That is **~1.5 s of main-thread disk I/O inside a 1.8 s start**, of which **~0.9 s was ours**. Both of
ours were mechanical to move — the cache directory is only needed when a preview copies something, and the
free-space line can arrive a frame later — and both are fixed now; what they bought, measured, is below.
### After the fix: what moved, and what did not
Both are now fixed — `scratchRoot` is a `Lazy<File>` resolved on first use, and the free-space probes run
in `Dispatchers.IO` and update the state when they return (`fillRootFreeSpace`, `fillFreeSpace`). Measured
afterwards:
| what | before | after |
|---|---|---|
| StrictMode at start, ours | 492 ms `cacheDir`, 397 ms `StatFs` | **gone** |
| StrictMode at start, not ours | 477 ms typefaces, 54 ms × 2 MIUI font hook | 50–78 ms in pairs, all from `miui.util.font.FontSettings.loadFontSetting` / `TypefaceUtils.loadFontSettings` / `contentcatcher.InterceptorProxy.create` via `MainActivity.onCreate` |
| debug cold start, 5 runs | median 1 814 ms | median 1 803 ms (1 697 · 1 752 · 1 803 · 1 823 · 2 507) |
| `coldStart` on the benchmark build | 519.5 / 531.6 ms | 485.6 / 568.9 / 600.0 ms |
**The numbers did not improve, and that is worth stating rather than dressing up.** The two violations were
*first-call* costs: creating the cache directory happens once per install, and `StatFs` is slow when its
page cache is cold. Neither is paid on a warm device, which is what all five timed runs are — and the
run-to-run spread on this phone (±115 ms between the three benchmark runs above) is larger than anything
the change could have bought. What the fix does buy is that the main thread no longer does this work at
all, which is exactly the case the benchmark's *first* iteration shows: its max is 648 ms after a fresh
install, against 550 ms on a warm one.
The honest conclusion is that the remaining 1.8 s of a debug start is not made of these two calls. It is
R8-less code, Compose's first composition, the typefaces, and MIUI's font hook — and the release-like
build already pays 0.5 s, not 1.8 s. Making a *number* out of any further work needs either a quieter
device or many more runs; making the *app* faster needs a baseline profile (next section).
## Measured on demand: the `:benchmark` module