Improve resolved classes saving strategy
If we already have a non empty profile file it means that we already
saved once the resolved classes. So there's no need to hurry up and
start the profile saver eagerly after 2s.
Bug: 27600652
(cherry picked from commit c15e566b36170237f01ccefc12129c1578a02140)
Change-Id: Iecc730c25eab779efccbbde66432dbbc61192e8a
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index fd33248..3d3f3dd 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -37,7 +37,7 @@
static constexpr const uint64_t kRandomDelayMaxMs = 40 * 1000; // 40 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 kInitialDelayMs = 2 * 1000; // 2 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;
@@ -54,7 +54,6 @@
foreign_dex_profile_path_(foreign_dex_profile_path),
code_cache_last_update_time_ns_(0),
shutting_down_(false),
- first_profile_(true),
wait_lock_("ProfileSaver wait lock"),
period_condition_("ProfileSaver period condition", wait_lock_),
total_bytes_written_(0),
@@ -66,6 +65,13 @@
total_ns_of_work_(0),
total_number_of_foreign_dex_marks_(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.
@@ -88,14 +94,12 @@
uint64_t save_period_ms = kSavePeriodMs;
VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
-
- bool first_iteration = true;
while (!ShuttingDown(self)) {
uint64_t sleep_time_ms;
- if (first_iteration) {
+ if (save_resolved_classes_) {
// Sleep less long for the first iteration since we want to record loaded classes shortly
// after app launch.
- sleep_time_ms = kInitialDelayMs;
+ sleep_time_ms = kSaveResolvedClassesDelayMs;
} else {
const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
sleep_time_ms = save_period_ms + random_sleep_delay_ms;
@@ -111,7 +115,7 @@
uint64_t start = NanoTime();
- if (!ProcessProfilingInfo() && save_period_ms < kMaxBackoffMs) {
+ 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.
@@ -120,16 +124,16 @@
// Reset the period to the initial value as it's highly likely to JIT again.
save_period_ms = kSavePeriodMs;
}
- first_iteration = false;
+ save_resolved_classes_ = false;
total_ns_of_work_ += (NanoTime() - start);
}
}
-bool ProfileSaver::ProcessProfilingInfo() {
+bool ProfileSaver::ProcessProfilingInfo(bool save_resolved_classes) {
ScopedTrace trace(__PRETTY_FUNCTION__);
uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs();
- if (!first_profile_ && last_update_time_ns - code_cache_last_update_time_ns_
+ 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
@@ -145,6 +149,12 @@
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
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);
+ }
for (const auto& it : tracked_locations) {
if (ShuttingDown(Thread::Current())) {
return true;
@@ -157,24 +167,31 @@
jit_code_cache_->GetCompiledArtMethods(locations, methods);
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 && !first_profile_) {
+ if (methods.size() < kMinimumNrOrMethodsToSave && !save_resolved_classes) {
VLOG(profiler) << "Not enough information to save to: " << filename
<<" Nr of methods: " << methods.size();
total_number_of_skipped_writes_++;
return false;
}
-
- std::set<DexCacheResolvedClasses> resolved_classes;
- if (first_profile_) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true);
- }
-
uint64_t bytes_written;
- if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods,
- resolved_classes,
- &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;
@@ -185,7 +202,6 @@
}
}
}
- first_profile_ = false;
return true;
}
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 48ab1ad..d810c81 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 ProcessProfilingInfo(bool save_resolved_classes);
// Returns true if the saver is shutting down (ProfileSaver::Stop() has been called).
bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
@@ -93,7 +93,7 @@
std::string app_data_dir_;
uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
- bool first_profile_ = true;
+ bool save_resolved_classes_;
// Save period condition support.
Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 472a85c..6a50b8e 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1459,6 +1459,14 @@
return stat(filename.c_str(), &buffer) == 0;
}
+bool FileExistsAndNotEmpty(const std::string& filename) {
+ struct stat buffer;
+ if (stat(filename.c_str(), &buffer) != 0) {
+ return false;
+ }
+ return buffer.st_size > 0;
+}
+
std::string PrettyDescriptor(Primitive::Type type) {
return PrettyDescriptor(Primitive::Descriptor(type));
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 83ac0b8..c1e88a4 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -296,6 +296,7 @@
// Returns true if the file exists.
bool FileExists(const std::string& filename);
+bool FileExistsAndNotEmpty(const std::string& filename);
class VoidFunctor {
public: