perf(ios): cache fontScale to avoid dispatch_sync on every measurement#495
Open
hryhoriiK97 wants to merge 4 commits into
Open
perf(ios): cache fontScale to avoid dispatch_sync on every measurement#495hryhoriiK97 wants to merge 4 commits into
hryhoriiK97 wants to merge 4 commits into
Conversation
ENRMFontScaleForMeasurement previously called dispatch_sync to the main thread on every Yoga measurement to read RCTFontSizeMultiplier(). In a scrolling feed with many markdown cells, this added one synchronous main-thread hop per cell per layout pass. Cache the value in a std::atomic<double> after the first read and update it via UIContentSizeCategoryDidChangeNotification. Subsequent measurements read the atomic directly with no thread synchronization. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
This PR optimizes iOS shadow-tree measurement by avoiding a main-thread dispatch_sync on every markdown measurement when retrieving the React Native font scale multiplier.
Changes:
- Adds a cached font-scale value backed by
std::atomic<double>and lazy initialization viastd::call_once. - Registers a Dynamic Type (
UIContentSizeCategoryDidChangeNotification) observer to refresh the cached value when the user changes content size category.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+25
to
+26
| static std::atomic<double> cachedScale{0.0}; | ||
| static std::once_flag flag; |
Comment on lines
6
to
9
| #include <algorithm> | ||
| #include <atomic> | ||
| #include <cmath> | ||
| #include <react/renderer/core/LayoutConstraints.h> |
…RGET_OS_OSX Co-authored-by: Cursor <cursoragent@cursor.com>
static inline gives each translation unit its own cachedScale and once_flag, causing duplicate dispatch_sync calls and notification observers. Plain inline (C++17) shares a single definition and static locals across all TUs. Co-authored-by: Cursor <cursoragent@cursor.com>
std::once_flag/std::call_once require <mutex>, and the #if !TARGET_OS_OSX guard requires <TargetConditionals.h> to be explicitly included rather than relying on transitive includes. Co-authored-by: Cursor <cursoragent@cursor.com>
| } else { | ||
| dispatch_sync(dispatch_get_main_queue(), readFontScale); | ||
| } | ||
| std::call_once(flag, [] { |
Comment on lines
+41
to
+51
| #if !TARGET_OS_OSX | ||
| dispatch_async(dispatch_get_main_queue(), ^{ | ||
| [[NSNotificationCenter defaultCenter] | ||
| addObserverForName:UIContentSizeCategoryDidChangeNotification | ||
| object:nil | ||
| queue:[NSOperationQueue mainQueue] | ||
| usingBlock:^(NSNotification *) { | ||
| cachedScale.store(RCTFontSizeMultiplier(), std::memory_order_relaxed); | ||
| }]; | ||
| }); | ||
| #endif |
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.
Summary
dispatch_syncto main thread for readingRCTFontSizeMultiplier()with a cachedstd::atomic<double>dispatch_sync; all subsequent measurements read the atomic directly — no main-thread hopUIContentSizeCategoryDidChangeNotificationobserver to update the cache when the user changes Dynamic Type font sizeTest plan
dispatch_syncto main inENRMFontScaleForMeasurementafter first callMade with Cursor