summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yurii Zubrytskyi <zyy@google.com> 2022-11-14 22:26:10 -0800
committer Yurii Zubrytskyi <zyy@google.com> 2022-11-30 18:04:32 -0800
commitdbce35604cdadfadc47492d7f75576365726d323 (patch)
tree504acb1394832a4e068150fc76b62baa59c477b7
parent591895bd28f3073dfeebf6bcfb0fc18491e54809 (diff)
[res] Reuse memory in RebuildFilterList()
Original code deleted all allocated arrays to new' them back right away. Now, with new methods in ByteBucketArray the code only clears the vectors without releasing the capacity, and then proceeds to free the vectors that ended up not being used. This speeds up theme changes (accents / dark theme etc) by about 20% + small other improvements in ByteBucketArray Bug: 237583012 Test: build + UTs + boot Change-Id: I158af793e5476b4f3215dbe602daa872136d633f
-rw-r--r--libs/androidfw/AssetManager2.cpp11
-rw-r--r--libs/androidfw/include/androidfw/ByteBucketArray.h50
-rw-r--r--libs/androidfw/tests/ByteBucketArray_test.cpp53
3 files changed, 99 insertions, 15 deletions
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 27b121b0cccc..2864c6843633 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1356,21 +1356,22 @@ base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
void AssetManager2::RebuildFilterList() {
for (PackageGroup& group : package_groups_) {
- for (ConfiguredPackage& impl : group.packages_) {
- impl.filtered_configs_.clear();
-
+ for (ConfiguredPackage& package : group.packages_) {
+ package.filtered_configs_.forEachItem([](auto, auto& fcg) { fcg.type_entries.clear(); });
// Create the filters here.
- impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
+ package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
FilteredConfigGroup* group = nullptr;
for (const auto& type_entry : type_spec.type_entries) {
if (type_entry.config.match(configuration_)) {
if (!group) {
- group = &impl.filtered_configs_.editItemAt(type_id - 1);
+ group = &package.filtered_configs_.editItemAt(type_id - 1);
}
group->type_entries.push_back(&type_entry);
}
}
});
+ package.filtered_configs_.trimBuckets(
+ [](const auto& fcg) { return fcg.type_entries.empty(); });
}
}
}
diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index 05a2c4db8fe9..ca0a9eda9caa 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -17,6 +17,7 @@
#ifndef __BYTE_BUCKET_ARRAY_H
#define __BYTE_BUCKET_ARRAY_H
+#include <algorithm>
#include <cstdint>
#include <cstring>
@@ -36,15 +37,11 @@ class ByteBucketArray {
}
~ByteBucketArray() {
- clear();
+ deleteBuckets();
}
void clear() {
- for (size_t i = 0; i < kNumBuckets; i++) {
- if (buckets_[i] != NULL) {
- delete[] buckets_[i];
- }
- }
+ deleteBuckets();
memset(buckets_, 0, sizeof(buckets_));
}
@@ -59,7 +56,7 @@ class ByteBucketArray {
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
+ if (bucket == nullptr) {
return default_;
}
return bucket[0x0f & static_cast<uint8_t>(index)];
@@ -70,9 +67,9 @@ class ByteBucketArray {
<< ") with size=" << size();
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
- T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
- bucket = buckets_[bucket_index] = new T[kBucketSize]();
+ T*& bucket = buckets_[bucket_index];
+ if (bucket == nullptr) {
+ bucket = new T[kBucketSize]();
}
return bucket[0x0f & static_cast<uint8_t>(index)];
}
@@ -86,9 +83,42 @@ class ByteBucketArray {
return true;
}
+ template <class Func>
+ void forEachItem(Func f) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ for (size_t j = 0; j < kBucketSize; j++) {
+ f((i << 4) | j, bucket[j]);
+ }
+ }
+ }
+ }
+
+ template <class Func>
+ void trimBuckets(Func isEmptyFunc) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ if (std::all_of(bucket, bucket + kBucketSize, isEmptyFunc)) {
+ delete[] bucket;
+ buckets_[i] = nullptr;
+ }
+ }
+ }
+ }
+
private:
enum { kNumBuckets = 16, kBucketSize = 16 };
+ void deleteBuckets() {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ if (buckets_[i] != nullptr) {
+ delete[] buckets_[i];
+ }
+ }
+ }
+
T* buckets_[kNumBuckets];
static inline const T default_ = {};
};
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
index 5d464c7dc0f7..9c36cfb212c5 100644
--- a/libs/androidfw/tests/ByteBucketArray_test.cpp
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -52,4 +52,57 @@ TEST(ByteBucketArrayTest, TestSparseInsertion) {
}
}
+TEST(ByteBucketArrayTest, TestForEach) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(10, 2));
+ ASSERT_TRUE(bba.set(26, 3));
+ ASSERT_TRUE(bba.set(129, 4));
+ ASSERT_TRUE(bba.set(234, 5));
+
+ int count = 0;
+ bba.forEachItem([&count](auto i, auto val) {
+ ++count;
+ switch (i) {
+ case 0:
+ EXPECT_EQ(1, val);
+ break;
+ case 10:
+ EXPECT_EQ(2, val);
+ break;
+ case 26:
+ EXPECT_EQ(3, val);
+ break;
+ case 129:
+ EXPECT_EQ(4, val);
+ break;
+ case 234:
+ EXPECT_EQ(5, val);
+ break;
+ default:
+ EXPECT_EQ(0, val);
+ break;
+ }
+ });
+ ASSERT_EQ(4 * 16, count);
+}
+
+TEST(ByteBucketArrayTest, TestTrimBuckets) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(255, 2));
+ {
+ bba.trimBuckets([](auto val) { return val < 2; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(1 * 16, count);
+ }
+ {
+ bba.trimBuckets([](auto val) { return val < 3; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(0, count);
+ }
+}
+
} // namespace android