Optimize iteration of ReadableNativeMaps

Summary:
Props are transferred from C++ to Java using ReadableNativeMaps. The current implementation of ReadableNativeMaps creates an internal HashMap the first time one of its methods is executed.
During the update of props ReadableNativeMaps are consumed only once and they are accessed through an Iterator. That's why there is no reason to create the internal HashMap, which is inefficient from performance and memory point of view.

This diff creates an experiment that avoids the creation of the internal HashMap during prop updates, additionally it removes a lock that was being used in the creation of the internal HashMap. I expect this change to have a positive impact in TTRC and memory (in particular for ME devices)

This diff reduces the amount of ReadableNativeMaps's internal HashMaps created during initial render of MP Home by ~35%.

Changelog: [Internal]

Reviewed By: JoshuaGross

Differential Revision: D25361169

fbshipit-source-id: 7b6efda11922d56127131494ca39b5cec75f1703
This commit is contained in:
David Vacca
2020-12-06 11:28:11 -08:00
committed by Facebook GitHub Bot
parent 158abfe8bf
commit 503a6f4463
2 changed files with 68 additions and 2 deletions
@@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.config.ReactFeatureFlags;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -88,6 +89,64 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
return mLocalTypeMap;
}
private Iterator<Map.Entry<String, Object>> createExperimentalIterator() {
if (mKeys == null) {
mKeys = Assertions.assertNotNull(importKeys());
}
final String[] iteratorKeys = mKeys;
final Object[] iteratorValues = Assertions.assertNotNull(importValues());
return new Iterator<Map.Entry<String, Object>>() {
int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < iteratorKeys.length;
}
@Override
public Map.Entry<String, Object> next() {
final int index = currentIndex++;
return new Map.Entry<String, Object>() {
@Override
public String getKey() {
return iteratorKeys[index];
}
@Override
public Object getValue() {
return iteratorValues[index];
}
@Override
public Object setValue(Object value) {
throw new UnsupportedOperationException(
"Can't set a value while iterating over a ReadableNativeMap");
}
};
}
};
}
private ReadableMapKeySetIterator createExperimentalKeySetIterator() {
if (mKeys == null) {
mKeys = Assertions.assertNotNull(importKeys());
}
final String[] iteratorKeys = mKeys;
return new ReadableMapKeySetIterator() {
int currentIndex = 0;
@Override
public boolean hasNextKey() {
return currentIndex < iteratorKeys.length;
}
@Override
public String nextKey() {
return iteratorKeys[currentIndex++];
}
};
}
private native Object[] importTypes();
@Override
@@ -187,12 +246,16 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
@Override
public @NonNull Iterator<Map.Entry<String, Object>> getEntryIterator() {
return getLocalMap().entrySet().iterator();
return ReactFeatureFlags.enableExperimentalReadableNativeMapIterator
? createExperimentalIterator()
: getLocalMap().entrySet().iterator();
}
@Override
public @NonNull ReadableMapKeySetIterator keySetIterator() {
return new ReadableNativeMapKeySetIterator(this);
return ReactFeatureFlags.enableExperimentalReadableNativeMapIterator
? createExperimentalKeySetIterator()
: new ReadableNativeMapKeySetIterator(this);
}
@Override