Repository navigation
Preserve lazy ViewManager initialization in UIManager - #58097
fairysword wants to merge 1 commit into
Conversation
Copy UIManager implementation property descriptors instead of spreading UIManagerImpl, so Bridgeless initialization does not eagerly invoke lazy ViewManager getters or trigger unnecessary getConstantsForViewManager calls. Add regression coverage verifying that ViewManager descriptors remain lazy, own, and enumerable, and that each getter is evaluated only once on first access.
UIManager lazy ViewManager initialization optimizationTest environment:
Metric definitions:
1. Temporary ReactInstance instrumentationThe following debug-only instrumentation was added locally to import android.util.Log
private fun createConstants(
viewManagers: List<ViewManager<in Nothing, in Nothing>>,
customDirectEvents: MutableMap<String, Any>?,
): MutableMap<String, Any> {
val startNanos = System.nanoTime()
// Existing implementation...
try {
return UIManagerModuleConstantsHelper.createConstants(
viewManagers,
null,
customDirectEvents,
)
} finally {
// Existing marker cleanup...
if (BuildConfig.DEBUG) {
Log.d(
"RNUIManagerPerf",
"getConstants(eagerViewManagers=${viewManagers.size}) took ${(System.nanoTime() - startNanos) / 1_000_000.0}ms",
)
}
}
}
private fun getConstantsForViewManager(
viewManager: ViewManager<*, *>,
customDirectEvents: MutableMap<String, Any>,
): NativeMap {
val startNanos = System.nanoTime()
// Existing implementation...
try {
val viewManagerConstants =
UIManagerModuleConstantsHelper.createConstantsForViewManager(
viewManager,
null,
null,
null,
customDirectEvents,
)
return Arguments.makeNativeMap(viewManagerConstants)
} finally {
// Existing trace cleanup...
if (BuildConfig.DEBUG) {
Log.d(
"RNUIManagerPerf",
"getConstantsForViewManager(${viewManager.name}) took ${(System.nanoTime() - startNanos) / 1_000_000.0}ms",
)
}
}
}2. Before/after logsBefore: object spreadWith The 20 Startup timing from the same run: After: property descriptor compositionAfter preserving the lazy property descriptors instead of reading their values, startup no longer calls Result:
Startup timing from the same run: The marker timings above are one cold-start sample and are included as supporting evidence. The deterministic regression signal is the removal of all 20 synchronous |
|
Hi maintainers — gentle ping on this PR, now open for just over two weeks. It preserves lazy ViewManager initialization in Bridgeless UIManager and includes a regression test verifying that getters are not eagerly invoked at module load. Happy to address any feedback or add coverage if helpful. Thanks! |
|
Hi @rubennorte — gentle follow-up on the review request from last week. This PR is still current and ready for review, with a regression test covering the eager ViewManager getter behavior. I’m happy to address any feedback or adjust the approach. Thanks for your time! |
TLDR: With the patch, the first screen appears about 58 ms soonerWe applied this change as a patch to React Native 0.86.3 in our app and measured it. Setup
Hermes sampling profiler (
Without the patch, every sample of Launch time: 10 cold starts per build, run back to back, same device.
"First frame" is We saw no crash or JS error at launch with the patch. |
|
Also when I asked Claude how it happened, it told me the following. It is an accident between two changes made a year apart:
This only runs when |
|
Approved. This is a critical fix which improves my app startup time about 28% |
|
Hi @javache — could you please advise whether this PR should remain with @rubennorte or be reassigned? The review request has been pending since September 9, with no formal review or import yet. The PR includes regression coverage, and other contributors have also reported that it improves first-screen startup. I’m happy to make any changes needed. Thank you! |
Summary:
Changelog:
[ANDROID] [FIXED] - Avoid unnecessary getConstantsForViewManager calls during UIManager initialization.
Test Plan:
yarn flow-checkyarn test packages/react-native/Libraries/ReactNative --runInBandyarn lint --quietyarn format-check