summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ryan Mitchell <rtmitchell@google.com> 2020-10-05 14:24:35 -0700
committer Ryan Mitchell <rtmitchell@google.com> 2020-11-12 08:13:48 -0800
commit87e89546b66da6d5098b46ff9b868ef82a753932 (patch)
treec99f5e1e0f712e5fcf88c928b8ed03f96b067392
parentc75c2e092218a7d77be39c89bfba7dd2b4823ac1 (diff)
Cache resolved theme values
When calling Resources#obtainStyledAttributes, if a value for an attribute is supplied from the theme, and the value in the theme is a reference to a resource, the reference will be resolved using AssetManager2::ResolveReference each time the value from the theme is selected. This causes Resources#obtainStyledAttributes to do repeated work every time the same attribute is supplied from the theme in multiple invocations. Caching the result of ResolveReference reduces the cost of this repeated work and reduces the amount of time needed to inflate views. Before: com.android.resources.perf (3 Tests) [1/3] com.android.resources.perf.PerfTest#youtube: PASSED (11.748s) youtube_ns_median: 95490747 youtube_ns_standardDeviation: 7282249 youtube_ns_mean: 98442515 [2/3] com.android.resources.perf.PerfTest#maps: PASSED (10.862s) maps_ns_standardDeviation: 4484213 maps_ns_mean: 87912988 maps_ns_median: 86325549 [3/3] com.android.resources.perf.PerfTest#gmail: PASSED (24.034s) gmail_ns_median: 282175838 gmail_ns_standardDeviation: 6560876 gmail_ns_mean: 282869146 After: com.android.resources.perf (3 Tests) [1/3] com.android.resources.perf.PerfTest#youtube: PASSED (11.245s) youtube_ns_median: 92292347 youtube_ns_standardDeviation: 5899906 youtube_ns_mean: 93045239 [2/3] com.android.resources.perf.PerfTest#maps: PASSED (10.583s) maps_ns_standardDeviation: 7567929 maps_ns_mean: 81895979 maps_ns_median: 78647883 [3/3] com.android.resources.perf.PerfTest#gmail: PASSED (21.439s) gmail_ns_median: 229185043 gmail_ns_standardDeviation: 8770133 gmail_ns_mean: 232561234 These tests were done on a Pixel 3 and with cpu settings configured by libs/hwui/tests/scripts/prep_generic.sh: Locked CPUs 4,5,6,7 to 1459200 / 2803200 KHz Disabled CPUs 0,1,2,3 Bug: 170232288 Test: atest ResourcesPerfWorkloads Change-Id: I1e9e9acaa40fa60475a0e55230e11243f5b69b39
-rw-r--r--libs/androidfw/AssetManager2.cpp31
-rw-r--r--libs/androidfw/AttributeResolution.cpp7
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h12
3 files changed, 36 insertions, 14 deletions
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 3e54dc67db76..d349628c2ab4 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -963,14 +963,25 @@ base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetRe
}
base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
- AssetManager2::SelectedValue& value) const {
+ AssetManager2::SelectedValue& value, bool cache_value) const {
if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) {
// Not a reference. Nothing to do.
return {};
}
- uint32_t combined_flags = value.flags;
- uint32_t resolve_resid = value.data;
+ const uint32_t original_flags = value.flags;
+ const uint32_t original_resid = value.data;
+ if (cache_value) {
+ auto cached_value = cached_resolved_values_.find(value.data);
+ if (cached_value != cached_resolved_values_.end()) {
+ value = cached_value->second;
+ value.flags |= original_flags;
+ return {};
+ }
+ }
+
+ uint32_t combined_flags = 0U;
+ uint32_t resolve_resid = original_resid;
constexpr const uint32_t kMaxIterations = 20;
for (uint32_t i = 0U;; i++) {
auto result = GetResource(resolve_resid, true /*may_be_bag*/);
@@ -981,9 +992,15 @@ base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
if (result->type != Res_value::TYPE_REFERENCE ||
result->data == Res_value::DATA_NULL_UNDEFINED ||
result->data == resolve_resid || i == kMaxIterations) {
- // This reference can't be resolved, so exit now and let the caller deal with it.
+ result->flags |= combined_flags;
+ if (cache_value) {
+ cached_resolved_values_[original_resid] = *result;
+ }
+
+ // Add the original flags after caching the result so queries with a different set of original
+ // flags do not include these original flags.
value = *result;
- value.flags |= combined_flags;
+ value.flags |= original_flags;
return {};
}
@@ -1353,6 +1370,8 @@ void AssetManager2::InvalidateCaches(uint32_t diff) {
++iter;
}
}
+
+ cached_resolved_values_.clear();
}
uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
@@ -1533,7 +1552,7 @@ base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference(
return base::unexpected(std::nullopt);
}
- auto resolve_result = asset_manager_->ResolveReference(*result);
+ auto resolve_result = asset_manager_->ResolveReference(*result, true /* cache_value */);
if (resolve_result.has_value()) {
result->flags |= value.flags;
value = *result;
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 71919fdbb736..347b4ec8346c 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -39,8 +39,7 @@ class XmlAttributeFinder
: public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
public:
explicit XmlAttributeFinder(const ResXMLParser* parser)
- : BackTrackingAttributeFinder(
- 0, parser != nullptr ? parser->getAttributeCount() : 0),
+ : BackTrackingAttributeFinder(0, parser != nullptr ? parser->getAttributeCount() : 0),
parser_(parser) {}
inline uint32_t GetAttribute(size_t index) const {
@@ -178,7 +177,7 @@ base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_
value = *attr_value;
DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
- const auto result = assetmanager->ResolveReference(value);
+ const auto result = assetmanager->ResolveReference(value, true /* cache_value */);
if (UNLIKELY(IsIOError(result))) {
return base::unexpected(GetIOError(result.error()));
}
@@ -310,7 +309,7 @@ base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* x
value = *attr_value;
DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
- auto result = assetmanager->ResolveReference(value);
+ auto result = assetmanager->ResolveReference(value, true /* cache_value */);
if (UNLIKELY(IsIOError(result))) {
return base::unexpected(GetIOError(result.error()));
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 4e993b0838a2..a92694c94b9f 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -264,11 +264,14 @@ class AssetManager2 {
// Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE.
//
// If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the
- // values pointed to by the reference are OR'd into `value.flags`.
+ // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then
+ // the resolved value will be cached and used when attempting to resolve the resource id specified
+ // in `value`.
//
// Returns a null error if the resource could not be resolved, or an I/O error if reading
// resource data failed.
- base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value) const;
+ base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value,
+ bool cache_value = false) const;
// Retrieves the best matching bag/map resource with ID `resid`.
//
@@ -446,13 +449,14 @@ class AssetManager2 {
// a number of times for each view during View inspection.
mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
+ // Cached set of resolved resource values.
+ mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_;
+
// Whether or not to save resource resolution steps
bool resource_resolution_logging_enabled_ = false;
struct Resolution {
-
struct Step {
-
enum class Type {
INITIAL,
BETTER_MATCH,