Merge "Improve ProfileSaver to cache data and do minimal I/O" into nyc-dev
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index 58e8a3a..ac18657 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -54,7 +54,7 @@
for (size_t i = 0; i < new_info.size(); i++) {
// Merge all data into a single object.
- if (!info.Load(new_info[i])) {
+ if (!info.MergeWith(new_info[i])) {
LOG(WARNING) << "Could not merge profile data at index " << i;
return kErrorBadProfiles;
}
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index b0d5df2..157ffc4 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -102,8 +102,8 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
@@ -145,9 +145,9 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Load(reference_info));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
+ ASSERT_TRUE(expected.MergeWith(reference_info));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index e805ce1..e864d87 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -49,6 +49,79 @@
}
}
+bool ProfileCompilationInfo::AddMethodsAndClasses(
+ const std::vector<ArtMethod*>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes) {
+ ScopedObjectAccess soa(Thread::Current());
+ for (ArtMethod* method : methods) {
+ const DexFile* dex_file = method->GetDexFile();
+ if (!AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
+ dex_file->GetLocationChecksum(),
+ method->GetDexMethodIndex())) {
+ return false;
+ }
+ }
+ for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
+ if (!AddResolvedClasses(dex_cache)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ProfileCompilationInfo::MergeAndSave(const std::string& filename, uint64_t* bytes_written) {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ ScopedFlock flock;
+ std::string error;
+ if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) {
+ LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
+ return false;
+ }
+
+ int fd = flock.GetFile()->Fd();
+
+ // Load the file but keep a copy around to be able to infer if the content has changed.
+ ProfileCompilationInfo fileInfo;
+ if (!fileInfo.Load(fd)) {
+ LOG(WARNING) << "Could not load previous profile data from file " << filename;
+ return false;
+ }
+
+ // Merge the content of file into the current object.
+ if (!MergeWith(fileInfo)) {
+ LOG(WARNING) << "Could not merge previous profile data from file " << filename;
+ }
+
+ // If after the merge we have the same data as what is the file there's no point
+ // in actually doing the write. The file will be exactly the same as before.
+ if (Equals(fileInfo)) {
+ if (bytes_written != nullptr) {
+ *bytes_written = 0;
+ }
+ return true;
+ }
+
+ // We need to clear the data because we don't support append to the profiles yet.
+ if (!flock.GetFile()->ClearContent()) {
+ PLOG(WARNING) << "Could not clear profile file: " << filename;
+ return false;
+ }
+
+ // This doesn't need locking because we are trying to lock the file for exclusive
+ // access and fail immediately if we can't.
+ bool result = Save(fd);
+ if (result) {
+ VLOG(profiler) << "Successfully saved profile info to " << filename
+ << " Size: " << GetFileSizeBytes(filename);
+ if (bytes_written != nullptr) {
+ *bytes_written = GetFileSizeBytes(filename);
+ }
+ } else {
+ VLOG(profiler) << "Failed to save profile info to " << filename;
+ }
+ return result;
+}
+
bool ProfileCompilationInfo::SaveProfilingInfo(
const std::string& filename,
const std::vector<ArtMethod*>& methods,
@@ -62,54 +135,16 @@
return true;
}
- ScopedTrace trace(__PRETTY_FUNCTION__);
- ScopedFlock flock;
- std::string error;
- if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) {
- LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
- return false;
- }
-
- int fd = flock.GetFile()->Fd();
-
ProfileCompilationInfo info;
- if (!info.Load(fd)) {
- LOG(WARNING) << "Could not load previous profile data from file " << filename;
- return false;
- }
- {
- ScopedObjectAccess soa(Thread::Current());
- for (ArtMethod* method : methods) {
- const DexFile* dex_file = method->GetDexFile();
- if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
- dex_file->GetLocationChecksum(),
- method->GetDexMethodIndex())) {
- return false;
- }
- }
- for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
- info.AddResolvedClasses(dex_cache);
- }
- }
-
- if (!flock.GetFile()->ClearContent()) {
- PLOG(WARNING) << "Could not clear profile file: " << filename;
- return false;
- }
-
- // This doesn't need locking because we are trying to lock the file for exclusive
- // access and fail immediately if we can't.
- bool result = info.Save(fd);
- if (result) {
- VLOG(profiler) << "Successfully saved profile info to " << filename
- << " Size: " << GetFileSizeBytes(filename);
+ if (!info.AddMethodsAndClasses(methods, resolved_classes)) {
+ LOG(WARNING) << "Checksum mismatch when processing methods and resolved classes for "
+ << filename;
if (bytes_written != nullptr) {
- *bytes_written = GetFileSizeBytes(filename);
+ *bytes_written = 0;
}
- } else {
- VLOG(profiler) << "Failed to save profile info to " << filename;
+ return false;
}
- return result;
+ return info.MergeAndSave(filename, bytes_written);
}
static bool WriteToFile(int fd, const std::ostringstream& os) {
@@ -341,7 +376,7 @@
return true;
}
-bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
+bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
for (const auto& other_it : other.info_) {
const std::string& other_dex_location = other_it.first;
const DexFileData& other_dex_data = other_it.second;
@@ -394,6 +429,14 @@
return total;
}
+uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
+ uint32_t total = 0;
+ for (const auto& it : info_) {
+ total += it.second.class_set.size();
+ }
+ return total;
+}
+
std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location) const {
std::ostringstream os;
@@ -449,4 +492,10 @@
return ret;
}
+void ProfileCompilationInfo::ClearResolvedClasses() {
+ for (auto& pair : info_) {
+ pair.second.class_set.clear();
+ }
+}
+
} // namespace art
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index fb07f8c..2c819f1 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -49,14 +49,23 @@
const std::set<DexCacheResolvedClasses>& resolved_classes,
uint64_t* bytes_written = nullptr);
+ // Add the given methods and classes to the current profile object.
+ bool AddMethodsAndClasses(const std::vector<ArtMethod*>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes);
// Loads profile information from the given file descriptor.
bool Load(int fd);
- // Loads the data from another ProfileCompilationInfo object.
- bool Load(const ProfileCompilationInfo& info);
+ // Merge the data from another ProfileCompilationInfo into the current object.
+ bool MergeWith(const ProfileCompilationInfo& info);
// Saves the profile data to the given file descriptor.
bool Save(int fd);
+ // Loads and merges profile information from the given file into the current
+ // object and tries to save it back to disk.
+ bool MergeAndSave(const std::string& filename, uint64_t* bytes_written);
+
// Returns the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
+ // Returns the number of resolved classes that were profiled.
+ uint32_t GetNumberOfResolvedClasses() const;
// Returns true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
@@ -71,8 +80,8 @@
std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location = true) const;
- // For testing purposes.
bool Equals(const ProfileCompilationInfo& other);
+
static std::string GetProfileDexFileKey(const std::string& dex_location);
// Returns the class descriptors for all of the classes in the profiles' class sets.
@@ -80,6 +89,9 @@
// profile info stuff to generate a map back to the dex location.
std::set<DexCacheResolvedClasses> GetResolvedClasses() const;
+ // Clears the resolved classes from the current object.
+ void ClearResolvedClasses();
+
private:
struct DexFileData {
explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index fdd8c6e..54fd69f 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -49,7 +49,7 @@
return methods;
}
- bool AddData(const std::string& dex_location,
+ bool AddMethod(const std::string& dex_location,
uint32_t checksum,
uint16_t method_index,
ProfileCompilationInfo* info) {
@@ -118,8 +118,8 @@
ProfileCompilationInfo saved_info;
// Save a few methods.
for (uint16_t i = 0; i < 10; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
ASSERT_EQ(0, profile.GetFile()->Flush());
@@ -132,9 +132,9 @@
// Save more methods.
for (uint16_t i = 0; i < 100; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(profile.GetFile()->ResetOffset());
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
@@ -147,25 +147,25 @@
ASSERT_TRUE(loaded_info2.Equals(saved_info));
}
-TEST_F(ProfileCompilationInfoTest, AddDataFail) {
+TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
ScratchFile profile;
ProfileCompilationInfo info;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
// Trying to add info for an existing file but with a different checksum.
- ASSERT_FALSE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
+ ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
}
-TEST_F(ProfileCompilationInfoTest, LoadFail) {
+TEST_F(ProfileCompilationInfoTest, MergeFail) {
ScratchFile profile;
ProfileCompilationInfo info1;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
// Use the same file, change the checksum.
ProfileCompilationInfo info2;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
- ASSERT_FALSE(info1.Load(info2));
+ ASSERT_FALSE(info1.MergeWith(info2));
}
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 3d3f3dd..81d81a5 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -29,18 +29,18 @@
// An arbitrary value to throttle save requests. Set to 2s for now.
static constexpr const uint64_t kMilisecondsToNano = 1000000;
-static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * kMilisecondsToNano;
// TODO: read the constants from ProfileOptions,
// Add a random delay each time we go to sleep so that we don't hammer the CPU
// with all profile savers running at the same time.
-static constexpr const uint64_t kRandomDelayMaxMs = 40 * 1000; // 40 seconds
+static constexpr const uint64_t kRandomDelayMaxMs = 30 * 1000; // 30 seconds
static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000; // 5 minutes
-static constexpr const uint64_t kSavePeriodMs = 40 * 1000; // 40 seconds
+static constexpr const uint64_t kSavePeriodMs = 20 * 1000; // 20 seconds
static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
static constexpr const double kBackoffCoef = 1.5;
-static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
@@ -52,8 +52,9 @@
const std::string& app_data_dir)
: jit_code_cache_(jit_code_cache),
foreign_dex_profile_path_(foreign_dex_profile_path),
- code_cache_last_update_time_ns_(0),
shutting_down_(false),
+ last_save_number_of_methods_(0),
+ last_save_number_of_classes__(0),
wait_lock_("ProfileSaver wait lock"),
period_condition_("ProfileSaver period condition", wait_lock_),
total_bytes_written_(0),
@@ -61,17 +62,14 @@
total_number_of_code_cache_queries_(0),
total_number_of_skipped_writes_(0),
total_number_of_failed_writes_(0),
- total_ns_of_sleep_(0),
+ total_ms_of_sleep_(0),
total_ns_of_work_(0),
- total_number_of_foreign_dex_marks_(0) {
+ total_number_of_foreign_dex_marks_(0),
+ max_number_of_profile_entries_cached_(0) {
AddTrackedLocations(output_filename, code_paths);
// We only need to save the resolved classes if the profile file is empty.
// Otherwise we must have already save them (we always do it during the first
// ever profile save).
- // TODO(calin) This only considers the case of the primary profile file.
- // Anything that gets loaded in the same VM will not have their resolved
- // classes save (unless they started before the initial saving was done).
- save_resolved_classes_ = !FileExistsAndNotEmpty(output_filename);
app_data_dir_ = "";
if (!app_data_dir.empty()) {
// The application directory is used to determine which dex files are owned by app.
@@ -94,9 +92,10 @@
uint64_t save_period_ms = kSavePeriodMs;
VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
+ bool cache_resolved_classes = true;
while (!ShuttingDown(self)) {
uint64_t sleep_time_ms;
- if (save_resolved_classes_) {
+ if (cache_resolved_classes) {
// Sleep less long for the first iteration since we want to record loaded classes shortly
// after app launch.
sleep_time_ms = kSaveResolvedClassesDelayMs;
@@ -108,41 +107,72 @@
MutexLock mu(self, wait_lock_);
period_condition_.TimedWait(self, sleep_time_ms, 0);
}
- total_ns_of_sleep_ += sleep_time_ms;
+ total_ms_of_sleep_ += sleep_time_ms;
if (ShuttingDown(self)) {
break;
}
uint64_t start = NanoTime();
-
- if (!ProcessProfilingInfo(save_resolved_classes_) && save_period_ms < kMaxBackoffMs) {
- // If we don't need to save now it is less likely that we will need to do
- // so in the future. Increase the time between saves according to the
- // kBackoffCoef, but make it no larger than kMaxBackoffMs.
- save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms);
+ if (cache_resolved_classes) {
+ // TODO(calin) This only considers the case of the primary profile file.
+ // Anything that gets loaded in the same VM will not have their resolved
+ // classes save (unless they started before the initial saving was done).
+ FetchAndCacheResolvedClasses();
} else {
- // Reset the period to the initial value as it's highly likely to JIT again.
- save_period_ms = kSavePeriodMs;
+ bool profile_saved_to_disk = ProcessProfilingInfo();
+ if (!profile_saved_to_disk && save_period_ms < kMaxBackoffMs) {
+ // If we don't need to save now it is less likely that we will need to do
+ // so in the future. Increase the time between saves according to the
+ // kBackoffCoef, but make it no larger than kMaxBackoffMs.
+ save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms);
+ } else {
+ // Reset the period to the initial value as it's highly likely to JIT again.
+ save_period_ms = kSavePeriodMs;
+ }
}
- save_resolved_classes_ = false;
+ cache_resolved_classes = false;
total_ns_of_work_ += (NanoTime() - start);
}
}
-bool ProfileSaver::ProcessProfilingInfo(bool save_resolved_classes) {
- ScopedTrace trace(__PRETTY_FUNCTION__);
- uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs();
- if (!save_resolved_classes && last_update_time_ns - code_cache_last_update_time_ns_
- < kMinimumTimeBetweenCodeCacheUpdatesNs) {
- VLOG(profiler) << "Not enough time has passed since the last code cache update."
- << "Last update: " << last_update_time_ns
- << " Last save: " << code_cache_last_update_time_ns_;
- total_number_of_skipped_writes_++;
- return false;
+ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) {
+ auto info_it = profile_cache_.find(filename);
+ if (info_it == profile_cache_.end()) {
+ info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
}
+ return &info_it->second;
+}
- code_cache_last_update_time_ns_ = last_update_time_ns;
+void ProfileSaver::FetchAndCacheResolvedClasses() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ std::set<DexCacheResolvedClasses> resolved_classes =
+ class_linker->GetResolvedClasses(/*ignore boot classes*/ true);
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ uint64_t total_number_of_profile_entries_cached = 0;
+ for (const auto& it : tracked_dex_base_locations_) {
+ std::set<DexCacheResolvedClasses> resolved_classes_for_location;
+ const std::string& filename = it.first;
+ const std::set<std::string>& locations = it.second;
+
+ for (const DexCacheResolvedClasses& classes : resolved_classes) {
+ if (locations.find(classes.GetDexLocation()) != locations.end()) {
+ resolved_classes_for_location.insert(classes);
+ }
+ }
+ ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
+ info->AddMethodsAndClasses(std::vector<ArtMethod*>(), resolved_classes_for_location);
+ total_number_of_profile_entries_cached += resolved_classes_for_location.size();
+ }
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+}
+
+bool ProfileSaver::ProcessProfilingInfo() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
SafeMap<std::string, std::set<std::string>> tracked_locations;
{
// Make a copy so that we don't hold the lock while doing I/O.
@@ -150,11 +180,8 @@
tracked_locations = tracked_dex_base_locations_;
}
- std::set<DexCacheResolvedClasses> resolved_classes;
- if (save_resolved_classes) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true);
- }
+ bool profile_file_saved = false;
+ uint64_t total_number_of_profile_entries_cached = 0;
for (const auto& it : tracked_locations) {
if (ShuttingDown(Thread::Current())) {
return true;
@@ -168,41 +195,53 @@
total_number_of_code_cache_queries_++;
}
- std::set<DexCacheResolvedClasses> resolved_classes_for_location;
- if (save_resolved_classes) {
- bool resolved_classes_already_in_file = FileExistsAndNotEmpty(filename);
- if (!resolved_classes_already_in_file) {
- for (const DexCacheResolvedClasses& classes : resolved_classes) {
- if (locations.find(classes.GetDexLocation()) != locations.end()) {
- resolved_classes_for_location.insert(classes);
- }
- }
- }
- }
- // Always save for the first one for loaded classes profile.
- if (methods.size() < kMinimumNrOrMethodsToSave && !save_resolved_classes) {
- VLOG(profiler) << "Not enough information to save to: " << filename
- <<" Nr of methods: " << methods.size();
+ ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
+ cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>());
+ uint32_t delta_number_of_methods =
+ cached_info->GetNumberOfMethods() - last_save_number_of_methods_;
+ uint32_t delta_number_of_classes =
+ cached_info->GetNumberOfResolvedClasses() - last_save_number_of_classes__;
+
+ if (delta_number_of_methods < kMinimumNumberOfMethodsToSave &&
+ delta_number_of_classes < kMinimumNumberOfClassesToSave) {
+ VLOG(profiler) << "Not enough information to save to: " << filename
+ << " Nr of methods: " << delta_number_of_methods
+ << " Nr of classes: " << delta_number_of_classes;
total_number_of_skipped_writes_++;
- return false;
+ continue;
}
uint64_t bytes_written;
- if (!ProfileCompilationInfo::SaveProfilingInfo(
- filename,
- methods,
- resolved_classes_for_location,
- &bytes_written)) {
- LOG(WARNING) << "Could not save profiling info to " << filename;
- total_number_of_failed_writes_++;
- return false;
- } else {
+ if (cached_info->MergeAndSave(filename, &bytes_written)) {
+ last_save_number_of_methods_ = cached_info->GetNumberOfMethods();
+ last_save_number_of_classes__ = cached_info->GetNumberOfResolvedClasses();
+ // Clear resolved classes. No need to store them around as
+ // they don't change after the first write.
+ cached_info->ClearResolvedClasses();
if (bytes_written > 0) {
total_number_of_writes_++;
total_bytes_written_ += bytes_written;
+ } else {
+ // At this point we could still have avoided the write.
+ // We load and merge the data from the file lazily at its first ever
+ // save attempt. So, whatever we are trying to save could already be
+ // in the file.
+ total_number_of_skipped_writes_++;
}
+ profile_file_saved = true;
+ } else {
+ LOG(WARNING) << "Could not save profiling info to " << filename;
+ total_number_of_failed_writes_++;
+ // TODO: (calin): if we failed because of bad profiling data or parsing
+ // errors we should clear the profile file.
}
+ total_number_of_profile_entries_cached +=
+ cached_info->GetNumberOfMethods() +
+ cached_info->GetNumberOfResolvedClasses();
}
- return true;
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+ return profile_file_saved;
}
void* ProfileSaver::RunProfileSaverThread(void* arg) {
@@ -431,12 +470,16 @@
void ProfileSaver::DumpInfo(std::ostream& os) {
os << "ProfileSaver total_bytes_written=" << total_bytes_written_ << '\n'
<< "ProfileSaver total_number_of_writes=" << total_number_of_writes_ << '\n'
- << "ProfileSaver total_number_of_code_cache_queries=" << total_number_of_code_cache_queries_ << '\n'
+ << "ProfileSaver total_number_of_code_cache_queries="
+ << total_number_of_code_cache_queries_ << '\n'
<< "ProfileSaver total_number_of_skipped_writes=" << total_number_of_skipped_writes_ << '\n'
<< "ProfileSaver total_number_of_failed_writes=" << total_number_of_failed_writes_ << '\n'
- << "ProfileSaver total_ms_of_sleep=" << (total_ns_of_sleep_ / kMilisecondsToNano) << '\n'
+ << "ProfileSaver total_ms_of_sleep=" << total_ms_of_sleep_ << '\n'
<< "ProfileSaver total_ms_of_work=" << (total_ns_of_work_ / kMilisecondsToNano) << '\n'
- << "ProfileSaver total_number_of_foreign_dex_marks=" << total_number_of_foreign_dex_marks_ << '\n';
+ << "ProfileSaver total_number_of_foreign_dex_marks="
+ << total_number_of_foreign_dex_marks_ << '\n'
+ << "ProfileSaver max_number_profile_entries_cached="
+ << max_number_of_profile_entries_cached_ << '\n';
}
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index d810c81..91390fe 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -65,7 +65,7 @@
void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_);
// Processes the existing profiling info from the jit code cache and returns
// true if it needed to be saved to disk.
- bool ProcessProfilingInfo(bool save_resolved_classes);
+ bool ProcessProfilingInfo();
// Returns true if the saver is shutting down (ProfileSaver::Stop() has been called).
bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
@@ -73,6 +73,14 @@
const std::vector<std::string>& code_paths)
REQUIRES(Locks::profiler_lock_);
+ // Retrieves the cached profile compilation info for the given profile file.
+ // If no entry exists, a new empty one will be created, added to the cache and
+ // then returned.
+ ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename);
+ // Fetches the current resolved classes from the ClassLinker and stores them
+ // in the profile_cache_ for later save.
+ void FetchAndCacheResolvedClasses();
+
static bool MaybeRecordDexUseInternal(
const std::string& dex_location,
const std::set<std::string>& tracked_locations,
@@ -91,9 +99,15 @@
GUARDED_BY(Locks::profiler_lock_);
std::string foreign_dex_profile_path_;
std::string app_data_dir_;
- uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
- bool save_resolved_classes_;
+ uint32_t last_save_number_of_methods_;
+ uint32_t last_save_number_of_classes__;
+
+ // A local cache for the profile information. Maps each tracked file to its
+ // profile information. The size of this cache is usually very small and tops
+ // to just a few hundreds entries in the ProfileCompilationInfo objects.
+ // It helps avoiding unnecessary writes to disk.
+ SafeMap<std::string, ProfileCompilationInfo> profile_cache_;
// Save period condition support.
Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -104,9 +118,11 @@
uint64_t total_number_of_code_cache_queries_;
uint64_t total_number_of_skipped_writes_;
uint64_t total_number_of_failed_writes_;
- uint64_t total_ns_of_sleep_;
+ uint64_t total_ms_of_sleep_;
uint64_t total_ns_of_work_;
uint64_t total_number_of_foreign_dex_marks_;
+ // TODO(calin): replace with an actual size.
+ uint64_t max_number_of_profile_entries_cached_;
DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
};