Reduce string allocations in ProfileCompilationInfo.
Use std::string_view internally to avoid std::string
allocations but keep external ProfileCompilationInfo API
unchanged with std::string to avoid object lifetime issues,
with the exception of `OfflineProfileMethodInfo` provided
to the compiler for AOT compilation.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: boots.
Bug: 181943478
Change-Id: If75c3ce16581760773bdaa3e94334c9c567745de
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index ddebd6d..4b8fb9d 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -171,7 +171,8 @@
// Note: this is OK because we don't store profiles of different apps into the same file.
// Apps with split apks don't cause trouble because each split has a different name and will not
// collide with other entries.
-std::string ProfileCompilationInfo::GetProfileDexFileBaseKey(const std::string& dex_location) {
+std::string_view ProfileCompilationInfo::GetProfileDexFileBaseKeyView(
+ std::string_view dex_location) {
DCHECK(!dex_location.empty());
size_t last_sep_index = dex_location.find_last_of('/');
if (last_sep_index == std::string::npos) {
@@ -182,12 +183,23 @@
}
}
-std::string ProfileCompilationInfo::GetBaseKeyFromAugmentedKey(
- const std::string& profile_key) {
+std::string ProfileCompilationInfo::GetProfileDexFileBaseKey(const std::string& dex_location) {
+ // Note: Conversions between std::string and std::string_view.
+ return std::string(GetProfileDexFileBaseKeyView(dex_location));
+}
+
+std::string_view ProfileCompilationInfo::GetBaseKeyViewFromAugmentedKey(
+ std::string_view profile_key) {
size_t pos = profile_key.rfind(kSampleMetadataSeparator);
return (pos == std::string::npos) ? profile_key : profile_key.substr(0, pos);
}
+std::string ProfileCompilationInfo::GetBaseKeyFromAugmentedKey(
+ const std::string& profile_key) {
+ // Note: Conversions between std::string and std::string_view.
+ return std::string(GetBaseKeyViewFromAugmentedKey(profile_key));
+}
+
std::string ProfileCompilationInfo::MigrateAnnotationInfo(
const std::string& base_key,
const std::string& augmented_key) {
@@ -681,9 +693,9 @@
const DexFile* dex_file,
const ProfileSampleAnnotation& annotation) const {
if (annotation == ProfileSampleAnnotation::kNone) {
- std::string profile_key = GetProfileDexFileBaseKey(dex_file->GetLocation());
+ std::string_view profile_key = GetProfileDexFileBaseKeyView(dex_file->GetLocation());
for (const DexFileData* dex_data : info_) {
- if (profile_key == GetBaseKeyFromAugmentedKey(dex_data->profile_key)) {
+ if (profile_key == GetBaseKeyViewFromAugmentedKey(dex_data->profile_key)) {
if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
return nullptr;
}
@@ -701,9 +713,9 @@
void ProfileCompilationInfo::FindAllDexData(
const DexFile* dex_file,
/*out*/ std::vector<const ProfileCompilationInfo::DexFileData*>* result) const {
- std::string profile_key = GetProfileDexFileBaseKey(dex_file->GetLocation());
+ std::string_view profile_key = GetProfileDexFileBaseKeyView(dex_file->GetLocation());
for (const DexFileData* dex_data : info_) {
- if (profile_key == GetBaseKeyFromAugmentedKey(dex_data->profile_key)) {
+ if (profile_key == GetBaseKeyViewFromAugmentedKey(dex_data->profile_key)) {
if (ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
result->push_back(dex_data);
}
@@ -1105,13 +1117,13 @@
}
bool ProfileCompilationInfo::VerifyProfileData(const std::vector<const DexFile*>& dex_files) {
- std::unordered_map<std::string, const DexFile*> key_to_dex_file;
+ std::unordered_map<std::string_view, const DexFile*> key_to_dex_file;
for (const DexFile* dex_file : dex_files) {
- key_to_dex_file.emplace(GetProfileDexFileBaseKey(dex_file->GetLocation()), dex_file);
+ key_to_dex_file.emplace(GetProfileDexFileBaseKeyView(dex_file->GetLocation()), dex_file);
}
for (const DexFileData* dex_data : info_) {
// We need to remove any annotation from the key during verification.
- const auto it = key_to_dex_file.find(GetBaseKeyFromAugmentedKey(dex_data->profile_key));
+ const auto it = key_to_dex_file.find(GetBaseKeyViewFromAugmentedKey(dex_data->profile_key));
if (it == key_to_dex_file.end()) {
// It is okay if profile contains data for additional dex files.
continue;
@@ -1681,7 +1693,7 @@
os << " [checksum=" << std::hex << dex_data->checksum << "]" << std::dec;
const DexFile* dex_file = nullptr;
for (const DexFile* current : dex_files) {
- if (GetBaseKeyFromAugmentedKey(dex_data->profile_key) == current->GetLocation() &&
+ if (GetBaseKeyViewFromAugmentedKey(dex_data->profile_key) == current->GetLocation() &&
dex_data->checksum == current->GetLocationChecksum()) {
dex_file = current;
}
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 7a994a6..622bec1 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -92,10 +92,13 @@
// (see compiler/optimizing/inliner.cc).
// A profile reference to the dex file (profile key, dex checksum and number of methods).
+ //
+ // This contains references to internal std::string data of a profile key. The profile
+ // key must not be modified or destroyed for the entire lifetime of a `DexReference`.
struct DexReference {
DexReference() : dex_checksum(0), num_method_ids(0) {}
- DexReference(const std::string& key, uint32_t checksum, uint32_t num_methods)
+ DexReference(std::string_view key, uint32_t checksum, uint32_t num_methods)
: profile_key(key), dex_checksum(checksum), num_method_ids(num_methods) {}
bool operator==(const DexReference& other) const {
@@ -106,11 +109,11 @@
bool MatchesDex(const DexFile* dex_file) const {
return dex_checksum == dex_file->GetLocationChecksum() &&
- GetBaseKeyFromAugmentedKey(profile_key) ==
- GetProfileDexFileBaseKey(dex_file->GetLocation());
+ GetBaseKeyViewFromAugmentedKey(profile_key) ==
+ GetProfileDexFileBaseKeyView(dex_file->GetLocation());
}
- std::string profile_key;
+ std::string_view profile_key;
uint32_t dex_checksum;
uint32_t num_method_ids;
};
@@ -286,9 +289,15 @@
};
// Encodes the full set of inline caches for a given method.
- // The dex_references vector is indexed according to the ClassReference::dex_profile_index.
+ //
+ // The `dex_references` vector is indexed according to the ClassReference::dex_profile_index.
// i.e. the dex file of any ClassReference present in the inline caches can be found at
// dex_references[ClassReference::dex_profile_index].
+ //
+ // The `dex_references` contains references to internal std::string data of profile keys.
+ // Those profile keys must not be modified or destroyed for the entire lifetime of the
+ // `OfflineProfileMethodInfo`. To ensure that, the `ProfileCompilationInfo` should not
+ // be modified or destroyed while an `OfflineProfileMethodInfo` is in use.
struct OfflineProfileMethodInfo {
explicit OfflineProfileMethodInfo(const InlineCacheMap* inline_cache_map)
: inline_caches(inline_cache_map) {}
@@ -935,6 +944,13 @@
// Returns the threshold size (in bytes) which will cause save/load failures.
size_t GetSizeErrorThresholdBytes() const;
+ // Implementation of `GetProfileDexFileBaseKey()` but returning a subview
+ // referencing the same underlying data to avoid excessive heap allocations.
+ static std::string_view GetProfileDexFileBaseKeyView(std::string_view dex_location);
+
+ // Implementation of `GetBaseKeyFromAugmentedKey()` but returning a subview
+ // referencing the same underlying data to avoid excessive heap allocations.
+ static std::string_view GetBaseKeyViewFromAugmentedKey(std::string_view dex_location);
// Returns the augmented profile key associated with the given dex location.
// The return key will contain a serialized form of the information from the provided
diff --git a/runtime/jit/profiling_info_test.cc b/runtime/jit/profiling_info_test.cc
index 319a3e1..efaea84 100644
--- a/runtime/jit/profiling_info_test.cc
+++ b/runtime/jit/profiling_info_test.cc
@@ -172,9 +172,14 @@
dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex());
if (dex_profile_index >= offline_pmi.dex_references.size()) {
// This is a new dex.
- const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileBaseKey(
- class_ref.dex_file->GetLocation());
- offline_pmi.dex_references.emplace_back(dex_key,
+ const std::string& location = class_ref.dex_file->GetLocation();
+ std::string dex_key = ProfileCompilationInfo::GetProfileDexFileBaseKey(location);
+ // The `dex_key` is a temporary that shall cease to exist soon. Create a view
+ // using the dex file's location as the backing data.
+ CHECK(EndsWith(location, dex_key));
+ size_t dex_key_start = location.size() - dex_key.size();
+ std::string_view dex_key_view(location.data() + dex_key_start, dex_key.size());
+ offline_pmi.dex_references.emplace_back(dex_key_view,
class_ref.dex_file->GetLocationChecksum(),
class_ref.dex_file->NumMethodIds());
}