diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/dex/frontend.cc | 20 | ||||
| -rw-r--r-- | compiler/dex/mir_analysis.cc | 8 | ||||
| -rw-r--r-- | compiler/dex/mir_graph.h | 5 | ||||
| -rw-r--r-- | compiler/dex/verification_results.cc | 2 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.cc | 94 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.h | 38 | ||||
| -rw-r--r-- | compiler/driver/compiler_options.h | 5 |
7 files changed, 161 insertions, 11 deletions
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 1c2d16f6ca..243395ad3d 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -17,6 +17,7 @@ #include "compiler_backend.h" #include "compiler_internals.h" #include "driver/compiler_driver.h" +#include "driver/compiler_options.h" #include "dataflow_iterator-inl.h" #include "leb128.h" #include "mirror/object.h" @@ -25,7 +26,7 @@ #include "backend.h" #include "base/logging.h" #include "base/timing_logger.h" - +#include "driver/compiler_options.h" #include "dex/quick/dex_file_to_method_inliner_map.h" namespace art { @@ -209,13 +210,26 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, cu.mir_graph->EnableOpcodeCounting(); } + const CompilerOptions& compiler_options = cu.compiler_driver->GetCompilerOptions(); + CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter(); + + // Check early if we should skip this compilation if using the profiled filter. + if (cu.compiler_driver->ProfilePresent()) { + std::string methodname = PrettyMethod(method_idx, dex_file); + if (cu.mir_graph->SkipCompilation(methodname)) { + return NULL; + } + } + /* Build the raw MIR graph */ cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); cu.NewTimingSplit("MIROpt:CheckFilters"); - if (cu.mir_graph->SkipCompilation()) { - return NULL; + if (compiler_filter != CompilerOptions::kInterpretOnly) { + if (cu.mir_graph->SkipCompilation()) { + return NULL; + } } /* Create the pass driver and launch it */ diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 667ee267ea..5314bb7025 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -999,7 +999,6 @@ bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) { /* * Will eventually want this to be a bit more sophisticated and happen at verification time. - * Ultimate goal is to drive with profile data. */ bool MIRGraph::SkipCompilation() { const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions(); @@ -1013,8 +1012,7 @@ bool MIRGraph::SkipCompilation() { return true; } - if (compiler_filter == CompilerOptions::kInterpretOnly) { - LOG(WARNING) << "InterpretOnly should ideally be filtered out prior to parsing."; + if (compiler_filter == CompilerOptions::kInterpretOnly || compiler_filter == CompilerOptions::kProfiled) { return true; } @@ -1170,4 +1168,8 @@ void MIRGraph::DoCacheFieldLoweringInfo() { } } +bool MIRGraph::SkipCompilation(const std::string& methodname) { + return cu_->compiler_driver->SkipCompilation(methodname); +} + } // namespace art diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 85d6d894b0..94b3816586 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -383,6 +383,11 @@ class MIRGraph { bool SkipCompilation(); /* + * Should we skip the compilation of this method based on its name? + */ + bool SkipCompilation(const std::string& methodname); + + /* * Parse dex method and add MIR at current insert point. Returns id (which is * actually the index of the method in the m_units_ array). */ diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 947c22d668..6b0875ccb7 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -110,7 +110,7 @@ bool VerificationResults::IsCandidateForCompilation(MethodReference& method_ref, if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { return false; } - return (compiler_options_->GetCompilerFilter() != CompilerOptions::kInterpretOnly); + return true; } } // namespace art diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index d3d58c919f..a46015d0f9 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -21,6 +21,7 @@ #include <vector> #include <unistd.h> +#include <fstream> #include "base/stl_util.h" #include "base/timing_logger.h" @@ -303,8 +304,9 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, - bool dump_stats, bool dump_passes, CumulativeLogger* timer) - : compiler_options_(compiler_options), + bool dump_stats, bool dump_passes, CumulativeLogger* timer, + std::string profile_file) + : profile_ok_(false), compiler_options_(compiler_options), verification_results_(verification_results), method_inliner_map_(method_inliner_map), compiler_backend_(CompilerBackend::Create(compiler_backend_kind)), @@ -338,6 +340,11 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key"); + // Read the profile file if one is provided. + if (profile_file != "") { + profile_ok_ = ReadProfile(profile_file); + } + dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX); compiler_backend_->Init(*this); @@ -1936,7 +1943,6 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t } else { MethodReference method_ref(&dex_file, method_idx); bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags); - if (compile) { // NOTE: if compiler declines to compile this method, it will return NULL. compiled_method = compiler_backend_->Compile( @@ -2073,4 +2079,86 @@ void CompilerDriver::InstructionSetToLLVMTarget(InstructionSet instruction_set, LOG(FATAL) << "Unknown instruction set: " << instruction_set; } } + +bool CompilerDriver::ReadProfile(const std::string& filename) { + VLOG(compiler) << "reading profile file " << filename; + struct stat st; + int err = stat(filename.c_str(), &st); + if (err == -1) { + VLOG(compiler) << "not found"; + return false; + } + std::ifstream in(filename.c_str()); + if (!in) { + VLOG(compiler) << "profile file " << filename << " exists but can't be opened"; + VLOG(compiler) << "file owner: " << st.st_uid << ":" << st.st_gid; + VLOG(compiler) << "me: " << getuid() << ":" << getgid(); + VLOG(compiler) << "file permissions: " << std::oct << st.st_mode; + VLOG(compiler) << "errno: " << errno; + return false; + } + // The first line contains summary information. + std::string line; + std::getline(in, line); + if (in.eof()) { + return false; + } + std::vector<std::string> summary_info; + Split(line, '/', summary_info); + if (summary_info.size() != 3) { + // Bad summary info. It should be count/total/bootpath + return false; + } + // This is the number of hits in all methods. + uint32_t total_count = 0; + for (int i = 0 ; i < 3; ++i) { + total_count += atoi(summary_info[0].c_str()); + } + + // Now read each line until the end of file. Each line consists of 3 fields separated by / + while (!in.eof()) { + std::getline(in, line); + if (in.eof()) { + break; + } + std::vector<std::string> info; + Split(line, '/', info); + if (info.size() != 3) { + // Malformed. + break; + } + const std::string& methodname = info[0]; + uint32_t count = atoi(info[1].c_str()); + uint32_t size = atoi(info[2].c_str()); + double percent = (count * 100.0) / total_count; + // Add it to the profile map + profile_map_[methodname] = ProfileData(methodname, count, size, percent); + } + return true; +} + +bool CompilerDriver::SkipCompilation(const std::string& method_name) { + if (!profile_ok_) { + return true; + } + constexpr double kThresholdPercent = 2.0; // Anything above this threshold will be compiled. + + // First find the method in the profile map. + ProfileMap::iterator i = profile_map_.find(method_name); + if (i == profile_map_.end()) { + // 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; + bool compile = data.IsAbove(kThresholdPercent); + if (compile) { + LOG(INFO) << "compiling method " << method_name << " because its usage is " << + data.GetPercent() << "%"; + } else { + VLOG(compiler) << "not compiling method " << method_name << " because usage is too low (" + << data.GetPercent() << "%)"; + } + return !compile; +} } // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index ac70e5aee0..12463a9b9d 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -105,7 +105,8 @@ class CompilerDriver { InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer); + CumulativeLogger* timer, + std::string profile_file = ""); ~CompilerDriver(); @@ -141,6 +142,10 @@ class CompilerDriver { return compiler_backend_.get(); } + bool ProfilePresent() const { + return profile_ok_; + } + // Are we compiling and creating an image file? bool IsImage() const { return image_; @@ -554,6 +559,37 @@ class CompilerDriver { return cfi_info_.get(); } + // Profile data. This is 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 { + public: + ProfileData() : count_(0), method_size_(0), percent_(0) {} + ProfileData(std::string method_name, uint32_t count, uint32_t method_size, double percent) : + method_name_(method_name), count_(count), method_size_(method_size), percent_(percent) { + } + + bool IsAbove(double v) const { return percent_ >= v; } + double GetPercent() const { return percent_; } + + private: + std::string method_name_; // Method name. + uint32_t count_; // Number number of times it has been called. + uint32_t method_size_; // Size of the method on dex instructions. + double percent_; // Percentage of time spent in this method. + }; + + // Profile data is stored in a map, indexed by the full method name. + typedef std::map<const std::string, ProfileData> ProfileMap; + ProfileMap profile_map_; + bool profile_ok_; + + // 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. + bool ReadProfile(const std::string& filename); + + // Should the compiler run on this method given profile information? + bool SkipCompilation(const std::string& method_name); + private: // Compute constant code and method pointers when possible void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 39738ab049..0cca1e9705 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -23,6 +23,7 @@ class CompilerOptions { public: enum CompilerFilter { kInterpretOnly, // Compile nothing. + kProfiled, // Compile based on profile. kSpace, // Maximize space savings. kBalanced, // Try to get the best performance return on compilation investment. kSpeed, // Maximize runtime performance. @@ -30,7 +31,11 @@ class CompilerOptions { }; // Guide heuristics to determine whether to compile method if profile data not available. +#if ART_SMALL_MODE + static const CompilerFilter kDefaultCompilerFilter = kProfiled; +#else static const CompilerFilter kDefaultCompilerFilter = kSpeed; +#endif static const size_t kDefaultHugeMethodThreshold = 10000; static const size_t kDefaultLargeMethodThreshold = 600; static const size_t kDefaultSmallMethodThreshold = 60; |