Skip to content

Commit

Permalink
Refactor caching of Spannable objects instide TextLayoutManager
Browse files Browse the repository at this point in the history
Summary:
This diff optimizes the caching of Spannable objects managed by the TextLayoutManager class.
Previously, these objects were cached using unsing a String representation of the RedableMap (creating this string adds a non trivial cost), this diff improves the caching performance relying on the equals / hashcode methods of the ReadableNativeMap class

I created a MC just to have a killswitch

Motivation: I was analysing another bug and I found this non performant code

changelog: [internal] internal

Reviewed By: shergin

Differential Revision: D23429365

fbshipit-source-id: 59e5ad0b1b95da992ac393aecfe029da68a8df97
  • Loading branch information
mdvacca authored and facebook-github-bot committed Sep 2, 2020
1 parent 045d8fe commit 7d6d5da
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,7 @@ public class ReactFeatureFlags {

/** Use experimental SetState retry mechanism in view? */
public static boolean enableExperimentalStateUpdateRetry = false;

/** Enable caching of Spannable objects using equality of ReadableNativeMaps */
public static boolean enableSpannableCacheByReadableNativeMapEquality = true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ rn_android_library(
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/config:config"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ViewProps;
Expand Down Expand Up @@ -61,6 +63,8 @@ public class TextLayoutManager {
private static final String MAXIMUM_NUMBER_OF_LINES_KEY = "maximumNumberOfLines";
private static final LruCache<String, Spannable> sSpannableCache =
new LruCache<>(spannableCacheSize);
private static final LruCache<ReadableNativeMap, Spannable> sSpannableCacheV2 =
new LruCache<>(spannableCacheSize);
private static final ConcurrentHashMap<Integer, Spannable> sTagToSpannableCache =
new ConcurrentHashMap<>();

Expand Down Expand Up @@ -179,20 +183,40 @@ public static Spannable getOrCreateSpannableForText(
@Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) {

Spannable preparedSpannableText;
String attributedStringPayload = attributedString.toString();
synchronized (sSpannableCacheLock) {
preparedSpannableText = sSpannableCache.get(attributedStringPayload);
// TODO: T31905686 implement proper equality of attributedStrings
if (preparedSpannableText != null) {
return preparedSpannableText;
String attributedStringPayload = "";

boolean cacheByReadableNativeMap =
ReactFeatureFlags.enableSpannableCacheByReadableNativeMapEquality;
// TODO: T74600554 Cleanup this experiment once positive impact is confirmed in production
if (cacheByReadableNativeMap) {
synchronized (sSpannableCacheLock) {
preparedSpannableText = sSpannableCacheV2.get((ReadableNativeMap) attributedString);
if (preparedSpannableText != null) {
return preparedSpannableText;
}
}
} else {
attributedStringPayload = attributedString.toString();
synchronized (sSpannableCacheLock) {
preparedSpannableText = sSpannableCache.get(attributedStringPayload);
if (preparedSpannableText != null) {
return preparedSpannableText;
}
}
}

preparedSpannableText =
createSpannableFromAttributedString(
context, attributedString, reactTextViewManagerCallback);
synchronized (sSpannableCacheLock) {
sSpannableCache.put(attributedStringPayload, preparedSpannableText);

if (cacheByReadableNativeMap) {
synchronized (sSpannableCacheLock) {
sSpannableCacheV2.put((ReadableNativeMap) attributedString, preparedSpannableText);
}
} else {
synchronized (sSpannableCacheLock) {
sSpannableCache.put(attributedStringPayload, preparedSpannableText);
}
}
return preparedSpannableText;
}
Expand Down

0 comments on commit 7d6d5da

Please sign in to comment.