Repository navigation
Trace one Android cold start per app in the perf probe, and profile the native launch - #210
Merged
Merged
Conversation
A Perfetto trace of a cold start on the CI emulator shows expo-updates holding up React Native. Its delay-load handler starts the updates controller only when MainActivity asks to load the app, then holds the load until the launcher has picked a bundle: the main thread finished activityResume at 1334 ms and scheduled the React surface at 1595 ms, right after the launcher logged its Check state. A config plugin now builds the controller in MainApplication.onCreate of a release build, after Expo's modules, and starts it on its own thread, so the launcher picks the bundle while the activity is created. initializeWithoutStarting only builds the controller, so the later initialize calls from the delay-load handler and onWillCreateReactInstance see it and return. start() opens the updates database, so it stays off the main thread. Debug builds are unchanged.
…ode helper, and fail the build if its anchor goes
On the same emulator A/B it moved nothing: - relaunch loop, 30 launches each (run 39917998701): cold start 2491 ms base, 2503 ms change; launch to ready 3756 ms base, 3723 ms change - passes, 5 each (run 39917763422): cold start 1713 ms base, 1728 ms change; verified cold start 1939 ms base, 2141 ms change The change trace shows why. initializeWithoutStarting kept the main thread asleep or waiting for a CPU for about 290 ms inside bindApplication, so the launcher started only about 35 ms earlier than before, and the thread that builds the React instance was created at 1151 ms against 1138 ms in the base trace. Starting all of it on a background thread could race the delay-load handler's own initialize.
Pull request footprint
User-delivered artifact sizes
Most complex product function
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The Android launch spends 1.3 to 1.8 s before React Native starts its runtime (launch to ready minus the ready mark, on the CI emulator). This PR profiles that span with Android's own tracing. The one cut the profile pointed at gave no gain on the A/B, so it was dropped, and the PR keeps only the tracing.
What changes
scripts/perf-probe.mjsrecords a Perfetto trace of one verified cold start per app. It is uploaded with the probe's artifacts ascold-start-base.perfetto-traceandcold-start-change.perfetto-trace. The config is inscripts/cold-start.pbtx.How the trace is taken:
perfetto --background-waitreturns once its data sources are running, so tracing has begun before the app starts.am start -Wreturns at the first frame, and SIGTERM then ends the trace.ps -p, because SELinux denies the shellkill -0on perfetto's domain. Shellsleeploops are banned by the flake-mask gate, and--detachreturns before the data sources start.The traces can be opened in ui.perfetto.dev or queried with
trace_processorand itsandroid.startupmodule (Perfetto: Android app startup).Profile
A verified cold start of the base app on the CI emulator, Perfetto run 39911621692. Startup lasted 2545 ms with tracing on. Times are ms from the launch intent.
ActivityThreadMainbindApplication: resources (78), binder setup (114), React Native's native librarieslibreactnativeandlibappmodules(155), content providers (under 10)activityStart, includingperformCreate:MainActivity(128)ReactInstance.initialize, includinginitTurboModules(94, about 60 of it building Expo module definitions)What the trace shows:
MainActivityasks to load the app, then holds the load untillaunchAssetFilereturns (UpdatesPackage.kt,EnabledUpdatesController.ktin expo-updates 57). In the trace the main thread finishedactivityResumeat 1334 ms and scheduled the React surface at 1595 ms, right after the launcher logged its Check state.Displayedtime andam start -WTotalTime end when the splash is hidden, not at the activity's first draw, since Android 12's splash screen API (Expo's lukmccall on expo/expo#36730). Ours is held until the runtime is built, so TotalTime includes JavaScript work.Tried and dropped: starting expo-updates in
Application.onCreateA config plugin built the updates controller in
MainApplication.onCreateof release builds and started it on its own thread. The aim was for the launcher to pick the bundle while the activity is created. Expo's own code starts it there under Android tests, and its older setup did too (Expo: EAS Update in existing native apps).initializeWithoutStartingkept the main thread asleep or waiting for a CPU for about 290 ms insidebindApplication. So the launcher started only about 35 ms earlier, and the React instance's thread was created at 1151 ms against 1138 ms.initialize, which is not synchronized. So the plugin is gone.Ruled out
expo-constantsandexpo-linkingby expo-router,react-native-workletsby Reanimated.Found for later, outside the native span
CookieManager. Cue's Trakt requests use a bearer token and no cookies.Neither belongs in this PR without its own measurement.