Skip to content

Commit a5e881c

Browse files
committed
ThemedResourceCache: Replace ArrayMap with HashMap for performance
When opening and closing activities in Settings, a significant amount of CPU time is spent looking up ArrayMap entries, as reported by simpleperf: 0.12% /system/framework/arm64/boot-framework.oat android.util.ArrayMap.binarySearchHashes ThemedResourceCache is responsible for a significant portion of the time spent in ArrayMap lookups: 0.08% 0.08% /system/framework/arm64/boot-framework.oat android.util.ArrayMap.binarySearchHashes | -- android.util.ArrayMap.binarySearchHashes | --50.00%-- android.util.ArrayMap.indexOf | |--36.71%-- android.util.ArrayMap.get | |--0.87%-- [hit in function] | | | |--9.64%-- android.content.res.ThemedResourceCache.getThemedLocked | | android.content.res.ThemedResourceCache.get | | | | | |--77.92%-- android.content.res.DrawableCache.getInstance | | | android.content.res.ResourcesImpl.loadDrawable | | | android.content.res.Resources.loadDrawable | | | android.content.res.TypedArray.getDrawableForDensity | | | android.content.res.Resources.getColor [DEDUPED] | | | | | | | |--62.94%-- android.view.View.<init> | | | | | | | | | |--64.58%-- android.view.ViewGroup.<init> | | | | | android.widget.LinearLayout.<init> | | | | | android.widget.LinearLayout.<init> | | | | | art_quick_invoke_stub | | | | | art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*) | | | | | art::InvokeConstructor(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ObjPtr<art::mirror::Object>, _jobject*) | | | | | art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*) | | | | | art_jni_trampoline | | | | | java.lang.reflect.Constructor.newInstance | | | | | android.view.LayoutInflater.createView | | | | | com.android.internal.policy.PhoneLayoutInflater.onCreateView | | | | | android.view.LayoutInflater.onCreateView | | | | | android.view.LayoutInflater.onCreateView | | | | | android.view.LayoutInflater.createViewFromTag | | | | | android.view.LayoutInflater.inflate | | | | | android.view.LayoutInflater.inflate | | | | | | | | | --35.42%-- android.widget.TextView.<init> | | | | android.widget.Button.<init> | | | | art_quick_invoke_stub | | | | art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*) | | | | art::InvokeConstructor(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ObjPtr<art::mirror::Object>, _jobject*) | | | | art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*) | | | | art_jni_trampoline | | | | java.lang.reflect.Constructor.newInstance | | | | android.view.LayoutInflater.createView | | | | com.android.internal.policy.PhoneLayoutInflater.onCreateView | | | | android.view.LayoutInflater.onCreateView | | | | android.view.LayoutInflater.onCreateView | | | | android.view.LayoutInflater.createViewFromTag | | | | android.view.LayoutInflater.rInflate | | | | android.view.LayoutInflater.rInflate | | | | android.view.LayoutInflater.rInflate | | | | android.view.LayoutInflater.inflate | | | | android.view.LayoutInflater.inflate | | | | android.view.LayoutInflater.inflate | | | | | | | --37.06%-- com.android.internal.widget.ToolbarWidgetWrapper.<init> | | | | | --22.08%-- android.content.res.ConfigurationBoundResourceCache.get | | android.content.res.ConfigurationBoundResourceCache.getInstance | | android.content.res.ResourcesImpl.loadComplexColorFromName | | android.content.res.ResourcesImpl.loadColorStateList | | android.content.res.Resources.loadColorStateList | | android.content.res.TypedArray.getColorStateList | | android.widget.TextView.readTextAppearance | | android.widget.TextView.setTextAppearance | | android.widget.TextView.setTextAppearance | | android.widget.Toolbar.setTitle | | com.android.wifi.x.com.android.internal.util.StateMachine$SmHandler.handleMessage | | android.view.SurfaceControl.copyFrom Empirical testing reveals that mThemedEntries usually contains around 14 entries, at which HashMap is 35% faster than ArrayMap for lookups and 54% faster [1] for insertions. The increased memory usage should be negligible at this size, so we can safely convert the map to a HashMap in order to improve performance in this hotpath. [1] https://docs.google.com/spreadsheets/d/136UJS2yVlZyPx30KDNgj4AWldkp9xbzIcWkLFj9RGgk/edit Test: simpleperf record -a; verify that ThemedResourceCache no longer appears under ArrayMap.binarySearchHashes Change-Id: I39e1c4b03fe0e60f933f02e253d2d3c4a483146f
1 parent ead7c07 commit a5e881c

1 file changed

Lines changed: 5 additions & 8 deletions

File tree

core/java/android/content/res/ThemedResourceCache.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
import android.content.pm.ActivityInfo.Config;
2323
import android.content.res.Resources.Theme;
2424
import android.content.res.Resources.ThemeKey;
25-
import android.util.ArrayMap;
2625
import android.util.LongSparseArray;
2726

2827
import java.lang.ref.WeakReference;
28+
import java.util.HashMap;
2929

3030
/**
3131
* Data structure used for caching data against themes.
@@ -34,7 +34,7 @@
3434
*/
3535
abstract class ThemedResourceCache<T> {
3636
@UnsupportedAppUsage
37-
private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
37+
private HashMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
3838
private LongSparseArray<WeakReference<T>> mUnthemedEntries;
3939
private LongSparseArray<WeakReference<T>> mNullThemedEntries;
4040

@@ -154,7 +154,7 @@ private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boo
154154

155155
if (mThemedEntries == null) {
156156
if (create) {
157-
mThemedEntries = new ArrayMap<>(1);
157+
mThemedEntries = new HashMap<>(1);
158158
} else {
159159
return null;
160160
}
@@ -199,11 +199,8 @@ private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) {
199199
private boolean prune(@Config int configChanges) {
200200
synchronized (this) {
201201
if (mThemedEntries != null) {
202-
for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
203-
if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
204-
mThemedEntries.removeAt(i);
205-
}
206-
}
202+
mThemedEntries.entrySet()
203+
.removeIf(entry -> pruneEntriesLocked(entry.getValue(), configChanges));
207204
}
208205

209206
pruneEntriesLocked(mNullThemedEntries, configChanges);

0 commit comments

Comments
 (0)