diff options
author | 2017-05-24 12:04:13 -0700 | |
---|---|---|
committer | 2017-06-02 18:25:09 +0000 | |
commit | ea650f3bc4f54eb2c647cf0f7134398ef5038792 (patch) | |
tree | cdddde74424cf9fbeb2ef62e6bebca619c5ed989 | |
parent | 6559ba1cfb9d24fdca4b1b1ec9e0b8dc31679982 (diff) |
Move profiles to use bitmaps for startup methods
This CL enables changes like compiling only hot methods while still
doing layout for hot and startup methods. The bitmaps are also a
bit smaller for post-launch use cases.
No change in compilation strategy yet.
Fixed some bugs in dexlayout test like using a profile with the wrong
dex location. This meant the second invocation of dexlayout didn't
have any profile data.
Added profman support for dump-classes-and-methods,
create-profile-from, and related test.
Profile sizes (bytes) post launch:
Gmail: 7290 -> 6136
Maps: 22896 -> 18984
Music: 8582 -> 7050
YouTube: 16733 -> 14592
Test: test-art-host
Bug: 62040831
Change-Id: I9915b81a2ff2c47464acbbdeb55ce30a33d5483f
-rw-r--r-- | compiler/driver/compiler_driver.cc | 3 | ||||
-rw-r--r-- | compiler/driver/compiler_driver_test.cc | 10 | ||||
-rw-r--r-- | dex2oat/dex2oat_test.cc | 4 | ||||
-rw-r--r-- | dexlayout/dexlayout.cc | 4 | ||||
-rw-r--r-- | dexlayout/dexlayout_test.cc | 20 | ||||
-rw-r--r-- | profman/profile_assistant_test.cc | 36 | ||||
-rw-r--r-- | profman/profman.cc | 97 | ||||
-rw-r--r-- | runtime/class_linker.cc | 3 | ||||
-rw-r--r-- | runtime/dex_cache_resolved_classes.h | 11 | ||||
-rw-r--r-- | runtime/jit/profile_compilation_info.cc | 181 | ||||
-rw-r--r-- | runtime/jit/profile_compilation_info.h | 117 | ||||
-rw-r--r-- | runtime/jit/profile_compilation_info_test.cc | 78 | ||||
-rw-r--r-- | runtime/jit/profile_saver.cc | 55 | ||||
-rw-r--r-- | runtime/safe_map.h | 6 |
14 files changed, 504 insertions, 121 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fc5f847354..0097f55e53 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1000,7 +1000,8 @@ bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_r if (profile_compilation_info_ == nullptr) { return false; } - bool result = profile_compilation_info_->ContainsMethod(method_ref); + // TODO: Revisit compiling all startup methods. b/36457259 + bool result = profile_compilation_info_->IsStartupOrHotMethod(method_ref); if (kDebugProfileGuidedCompilation) { LOG(INFO) << "[ProfileGuidedCompilation] " diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 26ea39f205..4b979d8125 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -239,8 +239,14 @@ class CompilerDriverProfileTest : public CompilerDriverTest { ProfileCompilationInfo info; for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { - profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 1); - profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 2); + profile_info_.AddMethodIndex(dex_file->GetLocation(), + dex_file->GetLocationChecksum(), + 1, + dex_file->NumMethodIds()); + profile_info_.AddMethodIndex(dex_file->GetLocation(), + dex_file->GetLocationChecksum(), + 2, + dex_file->NumMethodIds()); } return &profile_info_; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 40ca875fb6..b604e8b5f1 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -39,6 +39,8 @@ namespace art { +static constexpr size_t kMaxMethodIds = 65535; + using android::base::StringPrintf; class Dex2oatTest : public Dex2oatEnvironmentTest { @@ -613,7 +615,7 @@ class Dex2oatLayoutTest : public Dex2oatTest { ProfileCompilationInfo info; std::string profile_key = ProfileCompilationInfo::GetProfileDexFileKey(dex_location); for (size_t i = 0; i < num_classes; ++i) { - info.AddClassIndex(profile_key, checksum, dex::TypeIndex(1 + i)); + info.AddClassIndex(profile_key, checksum, dex::TypeIndex(1 + i), kMaxMethodIds); } bool result = info.Save(profile_test_fd); close(profile_test_fd); diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 205c0d1384..db227676c2 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -1557,7 +1557,7 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) { (method->GetAccessFlags() & kAccConstructor) != 0 && (method->GetAccessFlags() & kAccStatic) != 0; const bool method_executed = is_clinit || - info_->ContainsMethod(MethodReference(dex_file, method_id->GetIndex())); + info_->IsStartupOrHotMethod(MethodReference(dex_file, method_id->GetIndex())); if (!method_executed) { continue; } @@ -1699,7 +1699,7 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, (method->GetAccessFlags() & kAccConstructor) != 0 && (method->GetAccessFlags() & kAccStatic) != 0; const bool is_method_executed = is_clinit || - info_->ContainsMethod(MethodReference(dex_file, method_id->GetIndex())); + info_->IsStartupOrHotMethod(MethodReference(dex_file, method_id->GetIndex())); code_items[is_method_executed ? CodeItemKind::kMethodExecuted : CodeItemKind::kMethodNotExecuted] diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 1d09a7f72a..6fe8eeb66e 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -341,18 +341,30 @@ class DexLayoutTest : public CommonRuntimeTest { if ((i & 3) != 0) { pfi.AddMethodIndex(dex_location, dex_file->GetLocationChecksum(), - i); + i, + dex_file->NumMethodIds()); + ++profile_methods; + } else if ((i & 2) != 0) { + pfi.AddSampledMethod(/*startup*/true, + dex_location, + dex_file->GetLocationChecksum(), + i, + dex_file->NumMethodIds()); ++profile_methods; } } DexCacheResolvedClasses cur_classes(dex_location, dex_location, - dex_file->GetLocationChecksum()); + dex_file->GetLocationChecksum(), + dex_file->NumMethodIds()); // Add every even class too. for (uint32_t i = 0; i < dex_file->NumClassDefs(); i += 1) { - cur_classes.AddClass(dex_file->GetClassDef(i).class_idx_); - ++profile_classes; + if ((i & 2) == 0) { + cur_classes.AddClass(dex_file->GetClassDef(i).class_idx_); + ++profile_classes; + } } + classes.insert(cur_classes); } pfi.AddMethodsAndClasses(pmis, classes); // Write to provided file. diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 1c328987cb..e87852baf0 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -30,6 +30,8 @@ namespace art { +static constexpr size_t kMaxMethodIds = 65535; + class ProfileAssistantTest : public CommonRuntimeTest { public: void PostRuntimeCreate() OVERRIDE { @@ -56,15 +58,18 @@ class ProfileAssistantTest : public CommonRuntimeTest { GetOfflineProfileMethodInfo(dex_location1, dex_location_checksum1, dex_location2, dex_location_checksum2); if (reverse_dex_write_order) { - ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, pmi)); - ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, pmi)); + ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi)); + ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi)); } else { - ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, pmi)); - ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, pmi)); + ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi)); + ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi)); } } for (uint16_t i = 0; i < number_of_classes; i++) { - ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, dex::TypeIndex(i))); + ASSERT_TRUE(info->AddClassIndex(dex_location1, + dex_location_checksum1, + dex::TypeIndex(i), + kMaxMethodIds)); } ASSERT_TRUE(info->Save(GetFd(profile))); @@ -84,8 +89,8 @@ class ProfileAssistantTest : public CommonRuntimeTest { const std::string& dex_location2, uint32_t dex_checksum2) { ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); - pmi.dex_references.emplace_back(dex_location1, dex_checksum1); - pmi.dex_references.emplace_back(dex_location2, dex_checksum2); + pmi.dex_references.emplace_back(dex_location1, dex_checksum1, kMaxMethodIds); + pmi.dex_references.emplace_back(dex_location2, dex_checksum2, kMaxMethodIds); // Monomorphic for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { @@ -520,10 +525,11 @@ TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) { TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) { // Class names put here need to be in sorted order. std::vector<std::string> class_names = { + "HLjava/lang/Object;-><init>()V", "Ljava/lang/Comparable;", "Ljava/lang/Math;", "Ljava/lang/Object;", - "Ljava/lang/Object;-><init>()V" + "SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I", }; std::string file_contents; for (std::string& class_name : class_names) { @@ -807,15 +813,21 @@ TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) { // Verify that the start-up classes contain the invalid class. std::set<dex::TypeIndex> classes; - std::set<uint16_t> methods; - ASSERT_TRUE(info.GetClassesAndMethods(*dex_file, &classes, &methods)); + std::set<uint16_t> hot_methods; + std::set<uint16_t> startup_methods; + std::set<uint16_t> post_start_methods; + ASSERT_TRUE(info.GetClassesAndMethods(*dex_file, + &classes, + &hot_methods, + &startup_methods, + &post_start_methods)); ASSERT_EQ(1u, classes.size()); ASSERT_TRUE(classes.find(invalid_class_index) != classes.end()); // Verify that the invalid method is in the profile. - ASSERT_EQ(2u, methods.size()); + ASSERT_EQ(2u, hot_methods.size()); uint16_t invalid_method_index = std::numeric_limits<uint16_t>::max() - 1; - ASSERT_TRUE(methods.find(invalid_method_index) != methods.end()); + ASSERT_TRUE(hot_methods.find(invalid_method_index) != hot_methods.end()); } } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index afc21057b1..adef0d0332 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -43,6 +43,7 @@ #include "runtime.h" #include "type_reference.h" #include "utils.h" +#include "type_reference.h" #include "zip_archive.h" namespace art { @@ -150,6 +151,9 @@ static const std::string kClassAllMethods = "*"; static constexpr char kProfileParsingInlineChacheSep = '+'; static constexpr char kProfileParsingTypeSep = ','; static constexpr char kProfileParsingFirstCharInSignature = '('; +static constexpr char kMethodFlagStringHot = 'H'; +static constexpr char kMethodFlagStringStartup = 'S'; +static constexpr char kMethodFlagStringPostStartup = 'P'; // TODO(calin): This class has grown too much from its initial design. Split the functionality // into smaller, more contained pieces. @@ -426,18 +430,42 @@ class ProfMan FINAL { } for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) { std::set<dex::TypeIndex> class_types; - std::set<uint16_t> methods; - if (profile_info.GetClassesAndMethods(*dex_file.get(), &class_types, &methods)) { + std::set<uint16_t> hot_methods; + std::set<uint16_t> startup_methods; + std::set<uint16_t> post_startup_methods; + std::set<uint16_t> combined_methods; + if (profile_info.GetClassesAndMethods(*dex_file.get(), + &class_types, + &hot_methods, + &startup_methods, + &post_startup_methods)) { for (const dex::TypeIndex& type_index : class_types) { const DexFile::TypeId& type_id = dex_file->GetTypeId(type_index); out_lines->insert(std::string(dex_file->GetTypeDescriptor(type_id))); } - for (uint16_t dex_method_idx : methods) { + combined_methods = hot_methods; + combined_methods.insert(startup_methods.begin(), startup_methods.end()); + combined_methods.insert(post_startup_methods.begin(), post_startup_methods.end()); + for (uint16_t dex_method_idx : combined_methods) { const DexFile::MethodId& id = dex_file->GetMethodId(dex_method_idx); std::string signature_string(dex_file->GetMethodSignature(id).ToString()); std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_))); std::string method_name(dex_file->GetMethodName(id)); - out_lines->insert(type_string + kMethodSep + method_name + signature_string); + std::string flags_string; + if (hot_methods.find(dex_method_idx) != hot_methods.end()) { + flags_string += kMethodFlagStringHot; + } + if (startup_methods.find(dex_method_idx) != startup_methods.end()) { + flags_string += kMethodFlagStringStartup; + } + if (post_startup_methods.find(dex_method_idx) != post_startup_methods.end()) { + flags_string += kMethodFlagStringPostStartup; + } + out_lines->insert(flags_string + + type_string + + kMethodSep + + method_name + + signature_string); } } } @@ -461,7 +489,7 @@ class ProfMan FINAL { return true; } - int DumpClasses() { + int DumpClassesAndMethods() { // Validate that at least one profile file or reference was specified. if (profile_files_.empty() && profile_files_fd_.empty() && reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) { @@ -694,11 +722,30 @@ class ProfMan FINAL { /*out*/ProfileCompilationInfo* profile) { std::string klass; std::string method_str; - size_t method_sep_index = line.find(kMethodSep); + bool is_hot = false; + bool is_startup = false; + bool is_post_startup = false; + const size_t method_sep_index = line.find(kMethodSep, 0); if (method_sep_index == std::string::npos) { - klass = line; + klass = line.substr(0); } else { - klass = line.substr(0, method_sep_index); + // The method prefix flags are only valid for method strings. + size_t start_index = 0; + while (start_index < line.size() && line[start_index] != 'L') { + const char c = line[start_index]; + if (c == kMethodFlagStringHot) { + is_hot = true; + } else if (c == kMethodFlagStringStartup) { + is_startup = true; + } else if (c == kMethodFlagStringPostStartup) { + is_post_startup = true; + } else { + LOG(WARNING) << "Invalid flag " << c; + return false; + } + ++start_index; + } + klass = line.substr(start_index, method_sep_index - start_index); method_str = line.substr(method_sep_index + kMethodSep.size()); } @@ -715,7 +762,8 @@ class ProfMan FINAL { const auto& dex_resolved_classes = resolved_class_set.emplace( dex_file->GetLocation(), dex_file->GetBaseLocation(), - dex_file->GetLocationChecksum()); + dex_file->GetLocationChecksum(), + dex_file->NumMethodIds()); dex_resolved_classes.first->AddClass(class_ref.type_index); std::vector<ProfileMethodInfo> methods; if (method_str == kClassAllMethods) { @@ -745,6 +793,9 @@ class ProfMan FINAL { std::string method_spec; std::vector<std::string> inline_cache_elems; + // If none of the flags are set, default to hot. + is_hot = is_hot || (!is_hot && !is_startup && !is_post_startup); + std::vector<std::string> method_elems; bool is_missing_types = false; Split(method_str, kProfileParsingInlineChacheSep, &method_elems); @@ -766,7 +817,6 @@ class ProfMan FINAL { return false; } - std::vector<ProfileMethodInfo> pmi; std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches; if (is_missing_types || !inline_cache_elems.empty()) { uint32_t dex_pc; @@ -783,8 +833,29 @@ class ProfMan FINAL { } inline_caches.emplace_back(dex_pc, is_missing_types, classes); } - pmi.emplace_back(class_ref.dex_file, method_index, inline_caches); - profile->AddMethodsAndClasses(pmi, std::set<DexCacheResolvedClasses>()); + ProfileMethodInfo pmi(class_ref.dex_file, method_index, inline_caches); + if (is_hot) { + profile->AddMethod(pmi); + } + if (is_startup) { + if (!profile->AddSampledMethod(/*is_startup*/ true, + pmi.dex_file->GetLocation(), + pmi.dex_file->GetLocationChecksum(), + method_index, + pmi.dex_file->NumMethodIds())) { + return false; + } + DCHECK(profile->IsStartupOrHotMethod(MethodReference(pmi.dex_file, method_index))); + } + if (is_post_startup) { + if (!profile->AddSampledMethod(/*is_startup*/ false, + pmi.dex_file->GetLocation(), + pmi.dex_file->GetLocationChecksum(), + method_index, + pmi.dex_file->NumMethodIds())) { + return false; + } + } return true; } @@ -959,7 +1030,7 @@ static int profman(int argc, char** argv) { return profman.DumpProfileInfo(); } if (profman.ShouldOnlyDumpClassesAndMethods()) { - return profman.DumpClasses(); + return profman.DumpClassesAndMethods(); } if (profman.ShouldCreateProfile()) { return profman.CreateProfile(); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c169ac0733..c3a8fc5383 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8949,7 +8949,8 @@ class GetResolvedClassesVisitor : public ClassVisitor { last_dex_file_ = &dex_file; DexCacheResolvedClasses resolved_classes(dex_file.GetLocation(), dex_file.GetBaseLocation(), - dex_file.GetLocationChecksum()); + dex_file.GetLocationChecksum(), + dex_file.NumMethodIds()); last_resolved_classes_ = result_->find(resolved_classes); if (last_resolved_classes_ == result_->end()) { last_resolved_classes_ = result_->insert(resolved_classes).first; diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h index bebdf0dbfe..2278b052ed 100644 --- a/runtime/dex_cache_resolved_classes.h +++ b/runtime/dex_cache_resolved_classes.h @@ -30,10 +30,12 @@ class DexCacheResolvedClasses { public: DexCacheResolvedClasses(const std::string& dex_location, const std::string& base_location, - uint32_t location_checksum) + uint32_t location_checksum, + uint32_t num_method_ids) : dex_location_(dex_location), base_location_(base_location), - location_checksum_(location_checksum) {} + location_checksum_(location_checksum), + num_method_ids_(num_method_ids) {} // Only compare the key elements, ignore the resolved classes. int Compare(const DexCacheResolvedClasses& other) const { @@ -69,10 +71,15 @@ class DexCacheResolvedClasses { return classes_; } + size_t NumMethodIds() const { + return num_method_ids_; + } + private: const std::string dex_location_; const std::string base_location_; const uint32_t location_checksum_; + const uint32_t num_method_ids_; // Array of resolved class def indexes. mutable std::unordered_set<dex::TypeIndex> classes_; }; diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 86c15e6227..9c039e2270 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -49,7 +49,7 @@ namespace art { const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' }; // Last profile version: Instead of method index, put the difference with the last // method's index. -const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '7', '\0' }; +const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '8', '\0' }; static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -132,6 +132,33 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_ } } +bool ProfileCompilationInfo::AddSampledMethod(bool startup, + const std::string& dex_location, + uint32_t checksum, + uint16_t method_idx, + uint32_t num_method_ids) { + DexFileData* data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location), + checksum, + num_method_ids); + if (data == nullptr) { + return false; + } + data->AddSampledMethod(startup, method_idx); + return true; +} + +bool ProfileCompilationInfo::AddSampledMethods(bool startup, + std::vector<MethodReference>& methods) { + for (const MethodReference& ref : methods) { + DexFileData* data = GetOrAddDexFileData(ref.dex_file); + if (data == nullptr) { + return false; + } + data->AddSampledMethod(startup, ref.dex_method_index); + } + return true; +} + bool ProfileCompilationInfo::AddMethodsAndClasses( const std::vector<ProfileMethodInfo>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes) { @@ -252,7 +279,7 @@ static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) { static constexpr size_t kLineHeaderSize = 2 * sizeof(uint16_t) + // class_set.size + dex_location.size - 2 * sizeof(uint32_t); // method_map.size + checksum + 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids /** * Serialization format: @@ -297,7 +324,8 @@ bool ProfileCompilationInfo::Save(int fd) { required_capacity += kLineHeaderSize + dex_data.profile_key.size() + sizeof(uint16_t) * dex_data.class_set.size() + - methods_region_size; + methods_region_size + + dex_data.bitmap_storage.size(); } if (required_capacity > kProfileSizeErrorThresholdInBytes) { LOG(ERROR) << "Profile data size exceeds " @@ -335,10 +363,12 @@ bool ProfileCompilationInfo::Save(int fd) { DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max()); DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max()); + // Write profile line header. AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size())); AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size())); AddUintToBuffer(&buffer, methods_region_size); // uint32_t AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t + AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t AddStringToBuffer(&buffer, dex_data.profile_key); @@ -362,6 +392,10 @@ bool ProfileCompilationInfo::Save(int fd) { last_class_index = class_id.index_; AddUintToBuffer(&buffer, diff_with_last_class_index); } + + buffer.insert(buffer.end(), + dex_data.bitmap_storage.begin(), + dex_data.bitmap_storage.end()); } uint32_t output_size = 0; @@ -476,7 +510,8 @@ void ProfileCompilationInfo::GroupClassesByDex( ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData( const std::string& profile_key, - uint32_t checksum) { + uint32_t checksum, + uint32_t num_method_ids) { const auto profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size()); if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) { // Allow only 255 dex files to be profiled. This allows us to save bytes @@ -492,7 +527,11 @@ ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData if (info_.size() <= profile_index) { // This is a new addition. Add it to the info_ array. DexFileData* dex_file_data = new (&arena_) DexFileData( - &arena_, profile_key, checksum, profile_index); + &arena_, + profile_key, + checksum, + profile_index, + num_method_ids); info_.push_back(dex_file_data); } DexFileData* result = info_[profile_index]; @@ -500,6 +539,7 @@ ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData // This should always be the case since since the cache map is managed by ProfileCompilationInfo. DCHECK_EQ(profile_key, result->profile_key); DCHECK_EQ(profile_index, result->profile_index); + DCHECK_EQ(num_method_ids, result->num_method_ids); // Check that the checksum matches. // This may different if for example the dex file was updated and @@ -528,7 +568,7 @@ const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData( bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) { const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation()); const uint32_t checksum = classes.GetLocationChecksum(); - DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); + DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, classes.NumMethodIds()); if (data == nullptr) { return false; } @@ -538,15 +578,23 @@ bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& c bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location, uint32_t dex_checksum, - uint16_t method_index) { - return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo(nullptr)); + uint16_t method_index, + uint32_t num_method_ids) { + return AddMethod(dex_location, + dex_checksum, + method_index, + num_method_ids, + OfflineProfileMethodInfo(nullptr)); } bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, uint32_t dex_checksum, uint16_t method_index, + uint32_t num_method_ids, const OfflineProfileMethodInfo& pmi) { - DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location), dex_checksum); + DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location), + dex_checksum, + num_method_ids); if (data == nullptr) { // checksum mismatch return false; } @@ -579,7 +627,8 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index]; DexFileData* class_dex_data = GetOrAddDexFileData( GetProfileDexFileKey(dex_ref.dex_location), - dex_ref.dex_checksum); + dex_ref.dex_checksum, + dex_ref.num_method_ids); if (class_dex_data == nullptr) { // checksum mismatch return false; } @@ -590,9 +639,7 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, } bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { - DexFileData* const data = GetOrAddDexFileData( - GetProfileDexFileKey(pmi.dex_file->GetLocation()), - pmi.dex_file->GetLocationChecksum()); + DexFileData* const data = GetOrAddDexFileData(pmi.dex_file); if (data == nullptr) { // checksum mismatch return false; } @@ -604,9 +651,7 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { continue; } for (const TypeReference& class_ref : cache.classes) { - DexFileData* class_dex_data = GetOrAddDexFileData( - GetProfileDexFileKey(class_ref.dex_file->GetLocation()), - class_ref.dex_file->GetLocationChecksum()); + DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file); if (class_dex_data == nullptr) { // checksum mismatch return false; } @@ -623,8 +668,9 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location, uint32_t checksum, - dex::TypeIndex type_idx) { - DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); + dex::TypeIndex type_idx, + uint32_t num_method_ids) { + DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, num_method_ids); if (data == nullptr) { return false; } @@ -694,7 +740,9 @@ bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer, - line_header.method_region_size_bytes; uint16_t last_method_index = 0; while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) { - DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum); + DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, + line_header.checksum, + line_header.num_method_ids); uint16_t diff_with_last_method_index; READ_UINT(uint16_t, buffer, diff_with_last_method_index, error); uint16_t method_index = last_method_index + diff_with_last_method_index; @@ -729,7 +777,8 @@ bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer, last_class_index = type_index; if (!AddClassIndex(line_header.dex_location, line_header.checksum, - dex::TypeIndex(type_index))) { + dex::TypeIndex(type_index), + line_header.num_method_ids)) { return false; } } @@ -863,6 +912,7 @@ bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer, READ_UINT(uint16_t, buffer, line_header->class_set_size, error); READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error); READ_UINT(uint32_t, buffer, line_header->checksum, error); + READ_UINT(uint32_t, buffer, line_header->num_method_ids, error); return true; } @@ -902,7 +952,10 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine uint8_t number_of_dex_files, const ProfileLineHeader& line_header, /*out*/std::string* error) { - if (GetOrAddDexFileData(line_header.dex_location, line_header.checksum) == nullptr) { + DexFileData* data = GetOrAddDexFileData(line_header.dex_location, + line_header.checksum, + line_header.num_method_ids); + if (data == nullptr) { *error = "Error when reading profile file line header: checksum mismatch for " + line_header.dex_location; return kProfileLoadBadData; @@ -915,6 +968,16 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine if (!ReadClasses(buffer, line_header, error)) { return kProfileLoadBadData; } + + const size_t bytes = data->bitmap_storage.size(); + if (buffer.CountUnreadBytes() < bytes) { + *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation"; + return kProfileLoadBadData; + } + const uint8_t* base_ptr = buffer.GetCurrentPtr(); + std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]); + buffer.Advance(bytes); + // Read method bitmap. return kProfileLoadSuccess; } @@ -932,6 +995,15 @@ bool ProfileCompilationInfo::Load(int fd) { } } +void ProfileCompilationInfo::DexFileData::CreateBitmap() { + const size_t num_bits = num_method_ids * kMethodBitCount; + bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte); + if (!bitmap_storage.empty()) { + method_bitmap = + BitMemoryRegion(MemoryRegion(&bitmap_storage[0], bitmap_storage.size()), 0, num_bits); + } +} + // TODO(calin): fail fast if the dex checksums don't match. ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal( int fd, std::string* error) { @@ -1110,7 +1182,8 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { SafeMap<uint8_t, uint8_t> dex_profile_index_remap; for (const DexFileData* other_dex_data : other.info_) { const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key, - other_dex_data->checksum); + other_dex_data->checksum, + other_dex_data->num_method_ids); if (dex_data == nullptr) { return false; // Could happen if we exceed the number of allowed dex files. } @@ -1147,6 +1220,9 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { } } } + + // Merge the bitmaps. + dex_data->MergeBitmap(*other_dex_data); } return true; } @@ -1159,6 +1235,27 @@ static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) { return ChecksumMatch(dex_file.GetLocationChecksum(), checksum); } +bool ProfileCompilationInfo::IsStartupOrHotMethod(const MethodReference& method_ref) const { + return IsStartupOrHotMethod(method_ref.dex_file->GetLocation(), + method_ref.dex_file->GetLocationChecksum(), + method_ref.dex_method_index); +} + +bool ProfileCompilationInfo::IsStartupOrHotMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index) const { + const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location)); + if (dex_data == nullptr || !ChecksumMatch(dex_checksum, dex_data->checksum)) { + return false; + } + if (dex_data->HasSampledMethod(/*startup*/ true, dex_method_index)) { + return true; + } + const MethodMap& methods = dex_data->method_map; + const auto method_it = methods.find(dex_method_index); + return method_it != methods.end(); +} + bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { return FindMethod(method_ref.dex_file->GetLocation(), method_ref.dex_file->GetLocationChecksum(), @@ -1196,6 +1293,7 @@ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompila for (const DexFileData* dex_data : info_) { pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key; pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum; + pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids; } return pmi; @@ -1314,9 +1412,12 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* return os.str(); } -bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile& dex_file, - std::set<dex::TypeIndex>* class_set, - std::set<uint16_t>* method_set) const { +bool ProfileCompilationInfo::GetClassesAndMethods( + const DexFile& dex_file, + /*out*/std::set<dex::TypeIndex>* class_set, + /*out*/std::set<uint16_t>* hot_method_set, + /*out*/std::set<uint16_t>* startup_method_set, + /*out*/std::set<uint16_t>* post_startup_method_method_set) const { std::set<std::string> ret; std::string profile_key = GetProfileDexFileKey(dex_file.GetLocation()); const DexFileData* dex_data = FindDexData(profile_key); @@ -1324,7 +1425,15 @@ bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile& dex_file, return false; } for (const auto& it : dex_data->method_map) { - method_set->insert(it.first); + hot_method_set->insert(it.first); + } + for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) { + if (dex_data->HasSampledMethod(/*startup*/ true, method_idx)) { + startup_method_set->insert(method_idx); + } + if (dex_data->HasSampledMethod(/*startup*/ false, method_idx)) { + post_startup_method_method_set->insert(method_idx); + } } for (const dex::TypeIndex& type_index : dex_data->class_set) { class_set->insert(type_index); @@ -1366,7 +1475,10 @@ std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses( << ", profile checksum=" << dex_data->checksum; return std::set<DexCacheResolvedClasses>(); } - DexCacheResolvedClasses classes(dex_location, dex_location, dex_data->checksum); + DexCacheResolvedClasses classes(dex_location, + dex_location, + dex_data->checksum, + dex_data->num_method_ids); classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end()); ret.insert(classes); } @@ -1383,8 +1495,8 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd, const std::string base_dex_location = "base.apk"; ProfileCompilationInfo info; // The limits are defined by the dex specification. - uint16_t max_method = std::numeric_limits<uint16_t>::max(); - uint16_t max_classes = std::numeric_limits<uint16_t>::max(); + const uint16_t max_method = std::numeric_limits<uint16_t>::max(); + const uint16_t max_classes = std::numeric_limits<uint16_t>::max(); uint16_t number_of_methods = max_method * method_ratio / 100; uint16_t number_of_classes = max_classes * class_ratio / 100; @@ -1404,7 +1516,7 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd, if (m < (number_of_methods / kFavorSplit)) { method_idx %= kFavorFirstN; } - info.AddMethodIndex(profile_key, 0, method_idx); + info.AddMethodIndex(profile_key, 0, method_idx, max_method); } for (uint16_t c = 0; c < number_of_classes; c++) { @@ -1412,7 +1524,7 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd, if (c < (number_of_classes / kFavorSplit)) { type_idx %= kFavorFirstN; } - info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx)); + info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method); } } return info.Save(fd); @@ -1431,13 +1543,16 @@ bool ProfileCompilationInfo::GenerateTestProfile( for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { // Randomly add a class from the dex file (with 50% chance). if (std::rand() % 2 != 0) { - info.AddClassIndex(location, checksum, dex::TypeIndex(dex_file->GetClassDef(i).class_idx_)); + info.AddClassIndex(location, + checksum, + dex::TypeIndex(dex_file->GetClassDef(i).class_idx_), + dex_file->NumMethodIds()); } } for (uint32_t i = 0; i < dex_file->NumMethodIds(); ++i) { // Randomly add a method from the dex file (with 50% chance). if (std::rand() % 2 != 0) { - info.AddMethodIndex(location, checksum, i); + info.AddMethodIndex(location, checksum, i, dex_file->NumMethodIds()); } } } diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index ca5b28a809..2b89a41dd8 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -23,6 +23,7 @@ #include "atomic.h" #include "base/arena_object.h" #include "base/arena_containers.h" +#include "bit_memory_region.h" #include "dex_cache_resolved_classes.h" #include "dex_file.h" #include "dex_file_types.h" @@ -54,7 +55,9 @@ struct ProfileMethodInfo { ProfileMethodInfo(const DexFile* dex, uint32_t method_index, const std::vector<ProfileInlineCache>& caches) - : dex_file(dex), dex_method_index(method_index), inline_caches(caches) {} + : dex_file(dex), + dex_method_index(method_index), + inline_caches(caches) {} const DexFile* dex_file; const uint32_t dex_method_index; @@ -79,13 +82,15 @@ class ProfileCompilationInfo { // A dex location together with its checksum. struct DexReference { - DexReference() : dex_checksum(0) {} + DexReference() : dex_checksum(0), num_method_ids(0) {} - DexReference(const std::string& location, uint32_t checksum) - : dex_location(location), dex_checksum(checksum) {} + DexReference(const std::string& location, uint32_t checksum, uint32_t num_methods) + : dex_location(location), dex_checksum(checksum), num_method_ids(num_methods) {} bool operator==(const DexReference& other) const { - return dex_checksum == other.dex_checksum && dex_location == other.dex_location; + return dex_checksum == other.dex_checksum && + dex_location == other.dex_location && + num_method_ids == other.num_method_ids; } bool MatchesDex(const DexFile* dex_file) const { @@ -95,6 +100,7 @@ class ProfileCompilationInfo { std::string dex_location; uint32_t dex_checksum; + uint32_t num_method_ids; }; // Encodes a class reference in the profile. @@ -191,6 +197,24 @@ class ProfileCompilationInfo { bool AddMethodsAndClasses(const std::vector<ProfileMethodInfo>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes); + // Add a method index to the profile (without inline caches). + bool AddMethodIndex(const std::string& dex_location, + uint32_t checksum, + uint16_t method_idx, + uint32_t num_method_ids); + + // Add a method to the profile using its online representation (containing runtime structures). + bool AddMethod(const ProfileMethodInfo& pmi); + + // Add methods that have samples but are are not necessarily hot. These are partitioned into two + // possibly interesecting sets startup and post startup. + bool AddSampledMethods(bool startup, std::vector<MethodReference>& methods); + bool AddSampledMethod(bool startup, + const std::string& dex_location, + uint32_t checksum, + uint16_t method_idx, + uint32_t num_method_ids); + // Load profile information from the given file descriptor. // If the current profile is non-empty the load will fail. bool Load(int fd); @@ -216,6 +240,12 @@ class ProfileCompilationInfo { // Return the number of resolved classes that were profiled. uint32_t GetNumberOfResolvedClasses() const; + // Return true if the method reference is a hot or startup method in the profiling info. + bool IsStartupOrHotMethod(const MethodReference& method_ref) const; + bool IsStartupOrHotMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index) const; + // Return true if the method reference is present in the profiling info. bool ContainsMethod(const MethodReference& method_ref) const; @@ -244,7 +274,9 @@ class ProfileCompilationInfo { // file is register and has a matching checksum, false otherwise. bool GetClassesAndMethods(const DexFile& dex_file, /*out*/std::set<dex::TypeIndex>* class_set, - /*out*/std::set<uint16_t>* method_set) const; + /*out*/std::set<uint16_t>* hot_method_set, + /*out*/std::set<uint16_t>* startup_method_set, + /*out*/std::set<uint16_t>* post_startup_method_method_set) const; // Perform an equality test with the `other` profile information. bool Equals(const ProfileCompilationInfo& other); @@ -301,13 +333,31 @@ class ProfileCompilationInfo { DexFileData(ArenaAllocator* arena, const std::string& key, uint32_t location_checksum, - uint16_t index) + uint16_t index, + uint32_t num_methods) : arena_(arena), profile_key(key), profile_index(index), checksum(location_checksum), method_map(std::less<uint16_t>(), arena->Adapter(kArenaAllocProfile)), - class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)) {} + class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)), + num_method_ids(num_methods), + bitmap_storage(arena->Adapter(kArenaAllocProfile)) { + CreateBitmap(); + } + + bool operator==(const DexFileData& other) const { + return checksum == other.checksum && method_map == other.method_map; + } + + // Mark a method as executed at least once. + void AddSampledMethod(bool startup, size_t index) { + method_bitmap.StoreBit(MethodBitIndex(startup, index), true); + } + + bool HasSampledMethod(bool startup, size_t index) const { + return method_bitmap.LoadBit(MethodBitIndex(startup, index)); + } // The arena used to allocate new inline cache maps. ArenaAllocator* arena_; @@ -322,32 +372,64 @@ class ProfileCompilationInfo { // The classes which have been profiled. Note that these don't necessarily include // all the classes that can be found in the inline caches reference. ArenaSet<dex::TypeIndex> class_set; - - bool operator==(const DexFileData& other) const { - return checksum == other.checksum && method_map == other.method_map; - } - // Find the inline caches of the the given method index. Add an empty entry if // no previous data is found. InlineCacheMap* FindOrAddMethod(uint16_t method_index); + // Num method ids. + uint32_t num_method_ids; + ArenaVector<uint8_t> bitmap_storage; + BitMemoryRegion method_bitmap; + + void CreateBitmap(); + + void MergeBitmap(const DexFileData& other) { + DCHECK_EQ(bitmap_storage.size(), other.bitmap_storage.size()); + for (size_t i = 0; i < bitmap_storage.size(); ++i) { + bitmap_storage[i] |= other.bitmap_storage[i]; + } + } + + private: + enum Bits { + kMethodBitStartup, + kMethodBitAfterStartup, + kMethodBitCount, + }; + + size_t MethodBitIndex(bool startup, size_t index) const { + DCHECK_LT(index, num_method_ids); + if (!startup) { + index += num_method_ids; + } + return index; + } }; // Return the profile data for the given profile key or null if the dex location // already exists but has a different checksum - DexFileData* GetOrAddDexFileData(const std::string& profile_key, uint32_t checksum); + DexFileData* GetOrAddDexFileData(const std::string& profile_key, + uint32_t checksum, + uint32_t num_method_ids); - // Add a method to the profile using its online representation (containing runtime structures). - bool AddMethod(const ProfileMethodInfo& pmi); + DexFileData* GetOrAddDexFileData(const DexFile* dex_file) { + return GetOrAddDexFileData(GetProfileDexFileKey(dex_file->GetLocation()), + dex_file->GetLocationChecksum(), + dex_file->NumMethodIds()); + } // Add a method to the profile using its offline representation. // This is mostly used to facilitate testing. bool AddMethod(const std::string& dex_location, uint32_t dex_checksum, uint16_t method_index, + uint32_t num_method_ids, const OfflineProfileMethodInfo& pmi); // Add a class index to the profile. - bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx); + bool AddClassIndex(const std::string& dex_location, + uint32_t checksum, + dex::TypeIndex type_idx, + uint32_t num_method_ids); // Add all classes from the given dex cache to the the profile. bool AddResolvedClasses(const DexCacheResolvedClasses& classes); @@ -392,6 +474,7 @@ class ProfileCompilationInfo { uint16_t class_set_size; uint32_t method_region_size_bytes; uint32_t checksum; + uint32_t num_method_ids; }; // A helper structure to make sure we don't read past our buffers in the loops. diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 1cfa3552b9..615149feb3 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -32,6 +32,8 @@ namespace art { +static constexpr size_t kMaxMethodIds = 65535; + class ProfileCompilationInfoTest : public CommonRuntimeTest { public: void PostRuntimeCreate() OVERRIDE { @@ -61,7 +63,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { uint32_t checksum, uint16_t method_index, ProfileCompilationInfo* info) { - return info->AddMethodIndex(dex_location, checksum, method_index); + return info->AddMethodIndex(dex_location, checksum, method_index, kMaxMethodIds); } bool AddMethod(const std::string& dex_location, @@ -69,14 +71,14 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { uint16_t method_index, const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi, ProfileCompilationInfo* info) { - return info->AddMethod(dex_location, checksum, method_index, pmi); + return info->AddMethod(dex_location, checksum, method_index, kMaxMethodIds, pmi); } bool AddClass(const std::string& dex_location, uint32_t checksum, uint16_t class_index, ProfileCompilationInfo* info) { - return info->AddMethodIndex(dex_location, checksum, class_index); + return info->AddMethodIndex(dex_location, checksum, class_index, kMaxMethodIds); } uint32_t GetFd(const ScratchFile& file) { @@ -149,7 +151,9 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { std::vector<TypeReference> classes; caches.emplace_back(dex_pc, /*is_missing_types*/true, classes); } - ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches); + ProfileMethodInfo pmi(method->GetDexFile(), + method->GetDexMethodIndex(), + caches); profile_methods.push_back(pmi); profile_methods_map->Put(method, pmi); } @@ -191,7 +195,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( class_ref.dex_file->GetLocation()); offline_pmi.dex_references.emplace_back(dex_key, - class_ref.dex_file->GetLocationChecksum()); + class_ref.dex_file->GetLocationChecksum(), + class_ref.dex_file->NumMethodIds()); } } } @@ -201,6 +206,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { // Creates an offline profile used for testing inline caches. ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() { ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); + // Monomorphic for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); @@ -231,9 +237,9 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); - pmi.dex_references.emplace_back("dex_location1", /* checksum */1); - pmi.dex_references.emplace_back("dex_location2", /* checksum */2); - pmi.dex_references.emplace_back("dex_location3", /* checksum */3); + pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds); + pmi.dex_references.emplace_back("dex_location2", /* checksum */2, kMaxMethodIds); + pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds); return pmi; } @@ -694,8 +700,8 @@ TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) { ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); - pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); - pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2); + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds); + pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds); for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); dex_pc_data.AddClass(0, dex::TypeIndex(0)); @@ -705,8 +711,8 @@ TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) { ProfileCompilationInfo::InlineCacheMap* ic_map_reindexed = CreateInlineCacheMap(); ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(ic_map_reindexed); - pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2); - pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1); + pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds); + pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds); for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); dex_pc_data.AddClass(1, dex::TypeIndex(0)); @@ -761,7 +767,7 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) { // Create a megamorphic inline cache. ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); - pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds); ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); dex_pc_data.SetIsMegamorphic(); ic_map->Put(/*dex_pc*/ 0, dex_pc_data); @@ -791,7 +797,7 @@ TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) { // Create an inline cache with missing types ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); - pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds); ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); dex_pc_data.SetIsMissingTypes(); ic_map->Put(/*dex_pc*/ 0, dex_pc_data); @@ -839,4 +845,48 @@ TEST_F(ProfileCompilationInfoTest, LoadShouldClearExistingDataFromProfiles) { // This should fail since the test_info already contains data and the load would overwrite it. ASSERT_FALSE(test_info.Load(GetFd(profile))); } + +TEST_F(ProfileCompilationInfoTest, SampledMethodsTest) { + ProfileCompilationInfo test_info; + static constexpr size_t kNumMethods = 1000; + static constexpr size_t kChecksum1 = 1234; + static constexpr size_t kChecksum2 = 4321; + static const std::string kDex1 = "dex1"; + static const std::string kDex2 = "dex2"; + test_info.AddSampledMethod(true, kDex1, kChecksum1, 1, kNumMethods); + test_info.AddSampledMethod(true, kDex1, kChecksum1, 5, kNumMethods); + test_info.AddSampledMethod(false, kDex2, kChecksum2, 1, kNumMethods); + test_info.AddSampledMethod(false, kDex2, kChecksum2, 5, kNumMethods); + auto run_test = [](const ProfileCompilationInfo& info) { + EXPECT_FALSE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 0)); + EXPECT_TRUE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 1)); + EXPECT_FALSE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 3)); + EXPECT_TRUE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 5)); + EXPECT_FALSE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 6)); + EXPECT_FALSE(info.IsStartupOrHotMethod(kDex2, kChecksum2, 5)); + EXPECT_FALSE(info.IsStartupOrHotMethod(kDex2, kChecksum2, 5)); + }; + run_test(test_info); + + // Save the profile. + ScratchFile profile; + ASSERT_TRUE(test_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + + // Load the profile and make sure we can read the data and it matches what we expect. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + run_test(loaded_info); + + // Test that the bitmap gets merged properly. + EXPECT_FALSE(test_info.IsStartupOrHotMethod(kDex1, kChecksum1, 11)); + { + ProfileCompilationInfo merge_info; + merge_info.AddSampledMethod(true, kDex1, kChecksum1, 11, kNumMethods); + test_info.MergeWith(merge_info); + } + EXPECT_TRUE(test_info.IsStartupOrHotMethod(kDex1, kChecksum1, 11)); +} + } // namespace art diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index bc829cf9a7..c96ca88874 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -183,8 +183,11 @@ void ProfileSaver::NotifyJitActivityInternal() { // Excludes native methods and classes in the boot image. class GetMethodsVisitor : public ClassVisitor { public: - GetMethodsVisitor(std::vector<MethodReference>* methods, uint32_t startup_method_samples) - : methods_(methods), + GetMethodsVisitor(std::vector<MethodReference>* hot_methods, + std::vector<MethodReference>* startup_methods, + uint32_t startup_method_samples) + : hot_methods_(hot_methods), + startup_methods_(startup_methods), startup_method_samples_(startup_method_samples) {} virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -192,22 +195,26 @@ class GetMethodsVisitor : public ClassVisitor { return true; } for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { - if (!method.IsNative()) { - if (method.GetCounter() >= startup_method_samples_ || - method.GetProfilingInfo(kRuntimePointerSize) != nullptr || + if (!method.IsNative() && !method.IsProxyMethod()) { + const uint16_t counter = method.GetCounter(); + MethodReference ref(method.GetDexFile(), method.GetDexMethodIndex()); + if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr || (method.GetAccessFlags() & kAccPreviouslyWarm) != 0) { - // Have samples, add to profile. - const DexFile* dex_file = - method.GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetDexFile(); - methods_->push_back(MethodReference(dex_file, method.GetDexMethodIndex())); + hot_methods_->push_back(ref); + startup_methods_->push_back(ref); + } else if (counter >= startup_method_samples_) { + startup_methods_->push_back(ref); } + } else { + CHECK_EQ(method.GetCounter(), 0u); } } return true; } private: - std::vector<MethodReference>* const methods_; + std::vector<MethodReference>* const hot_methods_; + std::vector<MethodReference>* const startup_methods_; uint32_t startup_method_samples_; }; @@ -218,7 +225,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { ResolveTrackedLocations(); Thread* const self = Thread::Current(); - std::vector<MethodReference> methods; + std::vector<MethodReference> hot_methods; + std::vector<MethodReference> startup_methods; std::set<DexCacheResolvedClasses> resolved_classes; { ScopedObjectAccess soa(self); @@ -231,10 +239,13 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { { ScopedTrace trace2("Get hot methods"); - GetMethodsVisitor visitor(&methods, options_.GetStartupMethodSamples()); + GetMethodsVisitor visitor(&hot_methods, + &startup_methods, + options_.GetStartupMethodSamples()); class_linker->VisitClasses(&visitor); - VLOG(profiler) << "Methods with samples greater than " - << options_.GetStartupMethodSamples() << " = " << methods.size(); + VLOG(profiler) << "Profile saver recorded " << hot_methods.size() << " hot methods and " + << startup_methods.size() << " startup methods with threshold " + << options_.GetStartupMethodSamples(); } } MutexLock mu(self, *Locks::profiler_lock_); @@ -245,11 +256,18 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { const std::string& filename = it.first; const std::set<std::string>& locations = it.second; std::vector<ProfileMethodInfo> profile_methods_for_location; - for (const MethodReference& ref : methods) { + for (const MethodReference& ref : hot_methods) { if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) { profile_methods_for_location.emplace_back(ref.dex_file, ref.dex_method_index); } } + std::vector<MethodReference> startup_methods_for_locations; + for (const MethodReference& ref : startup_methods) { + if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) { + startup_methods_for_locations.push_back(ref); + } + } + for (const DexCacheResolvedClasses& classes : resolved_classes) { if (locations.find(classes.GetBaseLocation()) != locations.end()) { VLOG(profiler) << "Added " << classes.GetClasses().size() << " classes for location " @@ -265,8 +283,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { new ProfileCompilationInfo(Runtime::Current()->GetArenaPool())); ProfileCompilationInfo* cached_info = info_it->second; - cached_info->AddMethodsAndClasses(profile_methods_for_location, - resolved_classes_for_location); + cached_info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location); + cached_info->AddSampledMethods(/*startup*/ true, startup_methods_for_locations); total_number_of_profile_entries_cached += resolved_classes_for_location.size(); } max_number_of_profile_entries_cached_ = std::max( @@ -317,8 +335,7 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number uint64_t last_save_number_of_methods = info.GetNumberOfMethods(); uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses(); - info.AddMethodsAndClasses(profile_methods, - std::set<DexCacheResolvedClasses>()); + info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>()); auto profile_cache_it = profile_cache_.find(filename); if (profile_cache_it != profile_cache_.end()) { info.MergeWith(*(profile_cache_it->second)); diff --git a/runtime/safe_map.h b/runtime/safe_map.h index e638fdb504..b54f587715 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -46,6 +46,7 @@ class SafeMap { SafeMap() = default; SafeMap(const SafeMap&) = default; + SafeMap(SafeMap&&) = default; explicit SafeMap(const key_compare& cmp, const allocator_type& allocator = allocator_type()) : map_(cmp, allocator) { } @@ -151,6 +152,11 @@ class SafeMap { return map_ == rhs.map_; } + template <class... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + return map_.emplace(std::forward<Args>(args)...); + } + private: ::std::map<K, V, Comparator, Allocator> map_; }; |