文件
mynode/readme/dev/spec/mobile_startup_performance.md
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

127 行
3.2 KiB
Markdown

# Mobile startup performance
Android app can be slow to start on certain devices (about 3 seconds).
The app was analysed using Perfetto: https://github.com/laurent22/joplin/issues/14197
One fix was applied: upgrading expo-modules-core which had severe performance issues.
It appears that the rest of it cannot be optimised and is mostly due to the way React Native works and the size of the application.
More detailed report below:
## Why there’s no big optimisation left
### 1) The only abnormal cost is already fixed
You identified and removed the real regression:
* **ExpoFetchModule registration**
* Before: **~520 ms**
* After: **~4 ms**
* **Total Expo module registration**
* Before: **~1424 ms**
* After: **~61 ms**
That was the *one* clearly broken thing. It’s gone.
## What remains is normal, structural cost
### 2) React Native core initialisation is eager and synchronous
Measured costs:
* `ReactInstance.initialize` → **~266 ms**
* `getConstantsForViewManager` → **~254 ms**
* **46 ViewManagers**
* Cost scales roughly linearly with count
* `initTurboModules` → **~121 ms**
➡️ **~600–650 ms total**, before any UI can render.
You cannot:
* lazy-load ViewManagers
* lazy-load most TurboModules
* parallelise this work
This is **by design** in RN.
### 3) Native library loading is fixed overhead
Measured costs:
* `SoLoader.loadLibrary[appmodules]` → **~95 ms**
* `SoLoader.loadLibrary[expo-modules-core]` → **~9 ms**
* Hermes libraries → **~100+ ms total**
This is:
* native I/O + relocation
* one-time
* not affected by JS or RN changes
### 4) Android framework + IPC dominates the rest
Measured during startup window:
* Binder transactions + replies → **~1.6 s total**
* ~433 calls
* no single slow call (max ~46 ms)
* Animation / traversal / draw / present → **~1+ s combined**
This is:
* WindowManager
* ActivityManager
* SurfaceFlinger
* input / focus plumbing
➡️ **System-owned work**. You can’t optimise it from app code.
### 5) GC time stretches everything
Measured:
* GC during early startup → **~835 ms**
GC overlaps RN + system work, increasing *wall-clock* time, but:
* no single GC pause dominates
* mostly allocation pressure from startup churn
You already reduced the worst allocation source (Expo reflection).
## Why “small tweaks” don’t help anymore
* Lazy-loading JS → doesn’t affect native init
* Removing one Expo module → saves **single-digit ms**
* Chasing binder calls → symptoms, not cause
* Micro-optimising TurboModules → nothing stands out
What’s left is **many 100–300 ms chunks**, none removable alone.
## What this means in practice
* **Cold start ~2.5–3 s** for a medium/large RN app on Android is *normal*
* You’re now limited by:
* RN architecture (eager native init)
* Android startup pipeline
* native loading + GC
Further improvements are:
* **incremental** (baseline profile, trimming libs)
* **architectural** (smaller first render)
* or **perceptual** (change what “ready” means)
### Bottom line
Your profiling shows a **healthy but heavy startup**, not a bug.
You’ve already fixed the only clear regression; the remaining time is the unavoidable cost of RN + Android doing their jobs.