summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/dex/frontend.cc20
-rw-r--r--compiler/dex/mir_analysis.cc8
-rw-r--r--compiler/dex/mir_graph.h5
-rw-r--r--compiler/dex/verification_results.cc2
-rw-r--r--compiler/driver/compiler_driver.cc94
-rw-r--r--compiler/driver/compiler_driver.h38
-rw-r--r--compiler/driver/compiler_options.h5
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;