diff options
-rw-r--r-- | compiler/driver/compiler_driver.cc | 37 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.h | 2 | ||||
-rw-r--r-- | runtime/native/dalvik_system_DexFile.cc | 78 | ||||
-rw-r--r-- | runtime/profiler.cc | 27 | ||||
-rw-r--r-- | runtime/profiler.h | 76 | ||||
-rw-r--r-- | runtime/utils.cc | 19 | ||||
-rw-r--r-- | runtime/utils.h | 8 |
7 files changed, 122 insertions, 125 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8d4e2838a1..15a086b12d 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -35,6 +35,7 @@ #include "driver/compiler_options.h" #include "jni_internal.h" #include "object_utils.h" +#include "profiler.h" #include "runtime.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap.h" @@ -54,13 +55,10 @@ #include "thread_pool.h" #include "trampolines/trampoline_compiler.h" #include "transaction.h" +#include "utils.h" #include "verifier/method_verifier.h" #include "verifier/method_verifier-inl.h" -#ifdef HAVE_ANDROID_OS -#include "cutils/properties.h" -#endif - namespace art { static double Percentage(size_t x, size_t y) { @@ -369,7 +367,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, // Read the profile file if one is provided. if (profile_file != "") { - profile_ok_ = ProfileHelper::LoadProfileMap(profile_map_, profile_file); + profile_ok_ = profile_file_.LoadFile(profile_file); } dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX); @@ -2049,36 +2047,27 @@ bool CompilerDriver::SkipCompilation(const std::string& method_name) { if (!profile_ok_) { return false; } - // Methods that comprise topKPercentThreshold % of the total samples will be compiled. - double topKPercentThreshold = 90.0; -#ifdef HAVE_ANDROID_OS - char buf[PROP_VALUE_MAX]; - property_get("dalvik.vm.profile.compile_thr", buf, "90.0"); - topKPercentThreshold = strtod(buf, nullptr); -#endif - // Test for reasonable thresholds. - if (topKPercentThreshold < 10.0 || topKPercentThreshold > 90.0) { - topKPercentThreshold = 90.0; - } - - // First find the method in the profile map. - ProfileMap::iterator i = profile_map_.find(method_name); - if (i == profile_map_.end()) { + // First find the method in the profile file. + ProfileFile::ProfileData data; + if (!profile_file_.GetProfileData(&data, method_name)) { // Not in profile, no information can be determined. VLOG(compiler) << "not compiling " << method_name << " because it's not in the profile"; return true; } - const ProfileData& data = i->second; + + // Methods that comprise top_k_threshold % of the total samples will be compiled. + double top_k_threshold = GetDoubleProperty("dalvik.vm.profiler.compile_thr", 10.0, 90.0, 90.0); // Compare against the start of the topK percentage bucket just in case the threshold // falls inside a bucket. - bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent() <= topKPercentThreshold; + bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent() <= top_k_threshold; if (compile) { LOG(INFO) << "compiling method " << method_name << " because its usage is part of top " - << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%"; + << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%" + << " (topKThreshold=" << top_k_threshold << ")"; } else { VLOG(compiler) << "not compiling method " << method_name << " because it's not part of leading " - << topKPercentThreshold << "% samples)"; + << top_k_threshold << "% samples)"; } return !compile; } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 14ccb5047b..e952f63a63 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -595,7 +595,7 @@ class CompilerDriver { return cfi_info_.get(); } - ProfileMap profile_map_; + ProfileFile profile_file_; bool profile_ok_; // Should the compiler run on this method given profile information? diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index a0a294a69c..585c88eb4a 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -37,13 +37,10 @@ #include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" +#include "utils.h" #include "well_known_classes.h" #include "zip_archive.h" -#ifdef HAVE_ANDROID_OS -#include "cutils/properties.h" -#endif - namespace art { // A smart pointer that provides read-only access to a Java string's UTF chars. @@ -250,25 +247,6 @@ static void CopyProfileFile(const char* oldfile, const char* newfile) { close(fd2); } -static double GetDoubleProperty(const char* property, double minValue, double maxValue, double defaultValue) { -#ifndef HAVE_ANDROID_OS - return defaultValue; -#else - char buf[PROP_VALUE_MAX]; - char* endptr; - - property_get(property, buf, ""); - double value = strtod(buf, &endptr); - - if (value == 0 && endptr == buf) { - value = defaultValue; - } else if (value < minValue || value > maxValue) { - value = defaultValue; - } - return value; -#endif -} - static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, const char* pkgname, const char* instruction_set, const jboolean defer) { const bool kVerboseLogging = false; // Spammy logging. @@ -379,42 +357,46 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, // There is a previous profile file. Check if the profile has changed significantly. // A change in profile is considered significant if X% (change_thr property) of the top K% // (compile_thr property) samples has changed. - - double topKThreshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.compile_thr", 10.0, 90.0, 90.0); - double changeThreshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.change_thr", 1.0, 90.0, 10.0); - double changePercent = 0.0; - std::set<std::string> newTopK, oldTopK; - bool newOk = ProfileHelper::LoadTopKSamples(newTopK, profile_file, topKThreshold); - bool oldOk = ProfileHelper::LoadTopKSamples(oldTopK, prev_profile_file, topKThreshold); - if (!newOk || !oldOk) { + double top_k_threshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.compile_thr", 10.0, 90.0, 90.0); + double change_threshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.change_thr", 1.0, 90.0, 10.0); + double change_percent = 0.0; + ProfileFile new_profile, old_profile; + bool new_ok = new_profile.LoadFile(profile_file); + bool old_ok = old_profile.LoadFile(prev_profile_file); + if (!new_ok || !old_ok) { if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded Ignoring invalid profiles: " - << (newOk ? "" : profile_file) << " " << (oldOk ? "" : prev_profile_file); - } - } else if (newTopK.empty()) { - if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file; + << (new_ok ? "" : profile_file) << " " << (old_ok ? "" : prev_profile_file); } - // If the new topK is empty we shouldn't optimize so we leave the changePercent at 0.0. } else { - std::set<std::string> diff; - std::set_difference(newTopK.begin(), newTopK.end(), oldTopK.begin(), oldTopK.end(), - std::inserter(diff, diff.end())); - // TODO: consider using the usedPercentage instead of the plain diff count. - changePercent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(newTopK.size()); - if (kVerboseLogging) { - std::set<std::string>::iterator end = diff.end(); - for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) { - LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it; + std::set<std::string> new_top_k, old_top_k; + new_profile.GetTopKSamples(new_top_k, top_k_threshold); + old_profile.GetTopKSamples(old_top_k, top_k_threshold); + if (new_top_k.empty()) { + if (kVerboseLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file; + } + // If the new topK is empty we shouldn't optimize so we leave the change_percent at 0.0. + } else { + std::set<std::string> diff; + std::set_difference(new_top_k.begin(), new_top_k.end(), old_top_k.begin(), old_top_k.end(), + std::inserter(diff, diff.end())); + // TODO: consider using the usedPercentage instead of the plain diff count. + change_percent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(new_top_k.size()); + if (kVerboseLogging) { + std::set<std::string>::iterator end = diff.end(); + for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) { + LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it; + } } } } - if (changePercent > changeThreshold) { + if (change_percent > change_threshold) { if (kReasonLogging) { LOG(INFO) << "DexFile_isDexOptNeeded size of new profile file " << profile_file << " is significantly different from old profile file " << prev_profile_file << " (top " - << topKThreshold << "% samples changed in proportion of " << changePercent << "%)"; + << top_k_threshold << "% samples changed in proportion of " << change_percent << "%)"; } if (!defer) { CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str()); diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 5459ce3116..75db9da210 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -75,8 +75,6 @@ static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mu profiler->RecordMethod(method); } - - // A closure that is called by the thread checkpoint code. class SampleCheckpoint : public Closure { public: @@ -443,7 +441,7 @@ ProfileSampleResults::~ProfileSampleResults() { } } -// Add a method to the profile table. If it the first time the method +// Add a method to the profile table. If it's the first time the method // has been seen, add it with count=1, otherwise increment the count. void ProfileSampleResults::Put(mirror::ArtMethod* method) { lock_.Lock(Thread::Current()); @@ -578,7 +576,7 @@ void ProfileSampleResults::ReadPrevious(int fd) { } } -bool ProfileHelper::LoadProfileMap(ProfileMap& profileMap, const std::string& fileName) { +bool ProfileFile::LoadFile(const std::string& fileName) { LOG(VERBOSE) << "reading profile file " << fileName; struct stat st; int err = stat(fileName.c_str(), &st); @@ -629,7 +627,7 @@ bool ProfileHelper::LoadProfileMap(ProfileMap& profileMap, const std::string& fi Split(line, '/', info); if (info.size() != 3) { // Malformed. - break; + return false; } int count = atoi(info[1].c_str()); countSet.insert(std::make_pair(-count, info)); @@ -652,21 +650,24 @@ bool ProfileHelper::LoadProfileMap(ProfileMap& profileMap, const std::string& fi // Add it to the profile map. ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage); - profileMap[methodname] = curData; + profile_map_[methodname] = curData; prevData = &curData; } return true; } -bool ProfileHelper::LoadTopKSamples(std::set<std::string>& topKSamples, const std::string& fileName, - double topKPercentage) { - ProfileMap profileMap; - bool loadOk = LoadProfileMap(profileMap, fileName); - if (!loadOk) { +bool ProfileFile::GetProfileData(ProfileFile::ProfileData* data, const std::string& method_name) { + ProfileMap::iterator i = profile_map_.find(method_name); + if (i == profile_map_.end()) { return false; } - ProfileMap::iterator end = profileMap.end(); - for (ProfileMap::iterator it = profileMap.begin(); it != end; it++) { + *data = i->second; + return true; +} + +bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topKPercentage) { + ProfileMap::iterator end = profile_map_.end(); + for (ProfileMap::iterator it = profile_map_.begin(); it != end; it++) { if (it->second.GetTopKUsedPercentage() < topKPercentage) { topKSamples.insert(it->first); } diff --git a/runtime/profiler.h b/runtime/profiler.h index 938fdb7730..2502289c6a 100644 --- a/runtime/profiler.h +++ b/runtime/profiler.h @@ -188,52 +188,50 @@ class BackgroundMethodSamplingProfiler { DISALLOW_COPY_AND_ASSIGN(BackgroundMethodSamplingProfiler); }; -// TODO: incorporate in ProfileSampleResults - -// Profile data. This is generated from previous runs of the program and stored +// Contains profile data generated from previous runs of the program and stored // in a file. It is used to determine whether to compile a particular method or not. -class ProfileData { +class ProfileFile { public: - ProfileData() : count_(0), method_size_(0), usedPercent_(0) {} - ProfileData(const std::string& method_name, uint32_t count, uint32_t method_size, - double usedPercent, double topKUsedPercentage) : - method_name_(method_name), count_(count), method_size_(method_size), - usedPercent_(usedPercent), topKUsedPercentage_(topKUsedPercentage) { - // TODO: currently method_size_ and count_ are unused. - UNUSED(method_size_); - UNUSED(count_); - } + class ProfileData { + public: + ProfileData() : count_(0), method_size_(0), used_percent_(0) {} + ProfileData(const std::string& method_name, uint32_t count, uint32_t method_size, + double used_percent, double top_k_used_percentage) : + method_name_(method_name), count_(count), method_size_(method_size), + used_percent_(used_percent), top_k_used_percentage_(top_k_used_percentage) { + // TODO: currently method_size_ is unused + UNUSED(method_size_); + } + + double GetUsedPercent() const { return used_percent_; } + uint32_t GetCount() const { return count_; } + double GetTopKUsedPercentage() const { return top_k_used_percentage_; } + + private: + std::string method_name_; // Method name. + uint32_t count_; // Number of times it has been called. + uint32_t method_size_; // Size of the method on dex instructions. + double used_percent_; // Percentage of how many times this method was called. + double top_k_used_percentage_; // The percentage of the group that comprise K% of the total used + // methods this methods belongs to. + }; - bool IsAbove(double v) const { return usedPercent_ >= v; } - double GetUsedPercent() const { return usedPercent_; } - uint32_t GetCount() const { return count_; } - double GetTopKUsedPercentage() const { return topKUsedPercentage_; } + public: + // Loads profile data from the given file. The data are merged with any existing data. + // Returns true if the file was loaded successfully and false otherwise. + bool LoadFile(const std::string& filename); - private: - std::string method_name_; // Method name. - uint32_t count_; // Number of times it has been called. - uint32_t method_size_; // Size of the method on dex instructions. - double usedPercent_; // Percentage of how many times this method was called. - double topKUsedPercentage_; // The percentage of the group that comprise K% of the total used - // methods this methods belongs to. -}; + // Computes the group that comprise top_k_percentage of the total used methods. + bool GetTopKSamples(std::set<std::string>& top_k_methods, double top_k_percentage); -// Profile data is stored in a map, indexed by the full method name. -typedef std::map<std::string, ProfileData> ProfileMap; + // If the given method has an entry in the profile table it updates the data + // and returns true. Otherwise returns false and leaves the data unchanged. + bool GetProfileData(ProfileData* data, const std::string& method_name); -class ProfileHelper { private: - ProfileHelper(); - - public: - // Read the profile data from the given file. Calculates the percentage for each method. - // Returns false if there was no profile file or it was malformed. - static bool LoadProfileMap(ProfileMap& profileMap, const std::string& fileName); - - // Read the profile data from the given file and computes the group that comprise - // topKPercentage of the total used methods. - static bool LoadTopKSamples(std::set<std::string>& topKMethods, const std::string& fileName, - double topKPercentage); + // Profile data is stored in a map, indexed by the full method name. + typedef std::map<std::string, ProfileData> ProfileMap; + ProfileMap profile_map_; }; } // namespace art diff --git a/runtime/utils.cc b/runtime/utils.cc index f562252823..05ff5ffbdf 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1302,4 +1302,23 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { return true; } +double GetDoubleProperty(const char* property, double min_value, double max_value, double default_value) { +#ifndef HAVE_ANDROID_OS + return default_value; +#else + char buf[PROP_VALUE_MAX]; + char* endptr; + + property_get(property, buf, ""); + double value = strtod(buf, &endptr); + + // Return the defalt value if the string is invalid or the value is outside the given range + if ((value == 0 && endptr == buf) // Invalid string + || (value < min_value) || (value > max_value)) { // Out of range value + return default_value; + } + return value; +#endif +} + } // namespace art diff --git a/runtime/utils.h b/runtime/utils.h index 4a9236a4de..0f9b22bad4 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -28,6 +28,10 @@ #include "instruction_set.h" #include "primitive.h" +#ifdef HAVE_ANDROID_OS +#include "cutils/properties.h" +#endif + namespace art { class DexFile; @@ -439,6 +443,10 @@ class VoidFunctor { } }; +// Returns the given property as a double or its default_value if the property string is not valid +// or the parsed value is outside the interval [min_value, max_value]. +double GetDoubleProperty(const char* property, double min_value, double max_value, double default_value); + } // namespace art #endif // ART_RUNTIME_UTILS_H_ |