Add verify-profile compiler filter
Only verifies and dex2dex compiles classes in the profile. Goal
is to reduce application launch time.
~2x faster than interpret-only for Facebook.
Bug: 27688727
Change-Id: Ic9979c4f7ba4930298d0983c4e3c70c63500213f
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5d8e3ba..de000b0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -382,6 +382,9 @@
compiler_->Init();
+ if (compiler_options->VerifyOnlyProfile()) {
+ CHECK(profile_compilation_info_ != nullptr) << "Requires profile";
+ }
if (boot_image_) {
CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image";
}
@@ -829,10 +832,12 @@
const bool verification_enabled = compiler_options_->IsVerificationEnabled();
const bool never_verify = compiler_options_->NeverVerify();
+ const bool verify_only_profile = compiler_options_->VerifyOnlyProfile();
// We need to resolve for never_verify since it needs to run dex to dex to add the
// RETURN_VOID_NO_BARRIER.
- if (never_verify || verification_enabled) {
+ // Let the verifier resolve as needed for the verify_only_profile case.
+ if ((never_verify || verification_enabled) && !verify_only_profile) {
Resolve(class_loader, dex_files, timings);
VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
}
@@ -917,6 +922,22 @@
return result;
}
+bool CompilerDriver::ShouldVerifyClassBasedOnProfile(const DexFile& dex_file,
+ uint16_t class_idx) const {
+ if (!compiler_options_->VerifyOnlyProfile()) {
+ // No profile, verify everything.
+ return true;
+ }
+ DCHECK(profile_compilation_info_ != nullptr);
+ bool result = profile_compilation_info_->ContainsClass(dex_file, class_idx);
+ if (kDebugProfileGuidedCompilation) {
+ LOG(INFO) << "[ProfileGuidedCompilation] "
+ << (result ? "Verified" : "Skipped") << " method:"
+ << dex_file.GetClassDescriptor(dex_file.GetClassDef(class_idx));
+ }
+ return result;
+}
+
class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
public:
ResolveCatchBlockExceptionsClassVisitor(
@@ -2196,6 +2217,10 @@
ATRACE_CALL();
ScopedObjectAccess soa(Thread::Current());
const DexFile& dex_file = *manager_->GetDexFile();
+ if (!manager_->GetCompiler()->ShouldVerifyClassBasedOnProfile(dex_file, class_def_index)) {
+ // Skip verification since the class is not in the profile.
+ return;
+ }
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
const char* descriptor = dex_file.GetClassDescriptor(class_def);
ClassLinker* class_linker = manager_->GetClassLinker();
@@ -2661,6 +2686,7 @@
case mirror::Class::kStatusRetryVerificationAtRuntime:
case mirror::Class::kStatusVerified:
case mirror::Class::kStatusInitialized:
+ case mirror::Class::kStatusResolved:
break; // Expected states.
default:
LOG(FATAL) << "Unexpected class status for class "
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 42a5bc1..e40f7c0 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -91,7 +91,8 @@
Compiler::Kind compiler_kind,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- bool boot_image, std::unordered_set<std::string>* image_classes,
+ bool boot_image,
+ std::unordered_set<std::string>* image_classes,
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
size_t thread_count,
@@ -435,6 +436,10 @@
// according to the profile file.
bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const;
+ // Checks whether profile guided verification is enabled and if the method should be verified
+ // according to the profile file.
+ bool ShouldVerifyClassBasedOnProfile(const DexFile& dex_file, uint16_t class_idx) const;
+
void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
REQUIRES(!compiled_classes_lock_);
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 3bf8921..71eb787 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -197,6 +197,8 @@
compiler_filter_ = CompilerOptions::kEverything;
} else if (strcmp(compiler_filter_string, "time") == 0) {
compiler_filter_ = CompilerOptions::kTime;
+ } else if (strcmp(compiler_filter_string, "verify-profile") == 0) {
+ compiler_filter_ = CompilerOptions::kVerifyProfile;
} else {
Usage("Unknown --compiler-filter value %s", compiler_filter_string);
}
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 4db82a6..f13c7af 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -39,6 +39,7 @@
kSpeed, // Maximize runtime performance.
kEverything, // Force compilation of everything capable of being compiled.
kTime, // Compile methods, but minimize compilation time.
+ kVerifyProfile, // Verify only the classes in the profile.
};
// Guide heuristics to determine whether to compile method if profile data not available.
@@ -103,13 +104,14 @@
bool IsCompilationEnabled() const {
return compiler_filter_ != CompilerOptions::kVerifyNone &&
- compiler_filter_ != CompilerOptions::kInterpretOnly &&
- compiler_filter_ != CompilerOptions::kVerifyAtRuntime;
+ compiler_filter_ != CompilerOptions::kInterpretOnly &&
+ compiler_filter_ != CompilerOptions::kVerifyAtRuntime &&
+ compiler_filter_ != CompilerOptions::kVerifyProfile;
}
bool IsVerificationEnabled() const {
return compiler_filter_ != CompilerOptions::kVerifyNone &&
- compiler_filter_ != CompilerOptions::kVerifyAtRuntime;
+ compiler_filter_ != CompilerOptions::kVerifyAtRuntime;
}
bool NeverVerify() const {
@@ -120,6 +122,10 @@
return compiler_filter_ == CompilerOptions::kVerifyAtRuntime;
}
+ bool VerifyOnlyProfile() const {
+ return compiler_filter_ == CompilerOptions::kVerifyProfile;
+ }
+
size_t GetHugeMethodThreshold() const {
return huge_method_threshold_;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index d3b065c..976552a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -256,6 +256,7 @@
UsageError(" --compiler-filter="
"(verify-none"
"|verify-at-runtime"
+ "|verify-profile"
"|interpret-only"
"|space"
"|balanced"
@@ -263,6 +264,7 @@
"|everything"
"|time):");
UsageError(" select compiler filter.");
+ UsageError(" verify-profile requires a --profile(-fd) to also be passed in.");
UsageError(" Example: --compiler-filter=everything");
UsageError(" Default: speed");
UsageError("");
@@ -808,10 +810,16 @@
}
}
- if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) {
+ const bool have_profile_file = !profile_file_.empty();
+ const bool have_profile_fd = profile_file_fd_ != kInvalidFd;
+ if (have_profile_file && have_profile_fd) {
Usage("Profile file should not be specified with both --profile-file-fd and --profile-file");
}
+ if (compiler_options_->IsVerificationEnabled() && !have_profile_file && !have_profile_fd) {
+ Usage("verify-profile compiler filter must be used with a profile file or fd");
+ }
+
if (!parser_options->oat_symbols.empty()) {
oat_unstripped_ = std::move(parser_options->oat_symbols);
}
@@ -1453,8 +1461,8 @@
}
/*
- * If we're not in interpret-only or verify-none or verify-at-runtime mode, go ahead and
- * compile small applications. Don't bother to check if we're doing the image.
+ * If we're not in interpret-only or verify-none or verify-at-runtime or verify-profile mode,
+ * go ahead and compile small applications. Don't bother to check if we're doing the image.
*/
if (!IsBootImage() &&
compiler_options_->IsCompilationEnabled() &&
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 6a2b702..53d952f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3976,7 +3976,12 @@
oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
if (oat_file_class_status == mirror::Class::kStatusVerified ||
oat_file_class_status == mirror::Class::kStatusInitialized) {
- return true;
+ return true;
+ }
+ // If we only verified a subset of the classes at compile time, we can end up with classes that
+ // were resolved by the verifier.
+ if (oat_file_class_status == mirror::Class::kStatusResolved) {
+ return false;
}
if (oat_file_class_status == mirror::Class::kStatusRetryVerificationAtRuntime) {
// Compile time verification failed with a soft error. Compile time verification can fail
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 2d4777b..e805ce1 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -374,6 +374,18 @@
return false;
}
+bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const {
+ auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation()));
+ if (info_it != info_.end()) {
+ if (dex_file.GetLocationChecksum() != info_it->second.checksum) {
+ return false;
+ }
+ const std::set<uint16_t>& classes = info_it->second.class_set;
+ return classes.find(class_def_idx) != classes.end();
+ }
+ return false;
+}
+
uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
uint32_t total = 0;
for (const auto& it : info_) {
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index 52cab92..fb07f8c 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -61,6 +61,9 @@
// Returns true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
+ // Returns true if the class is present in the profiling info.
+ bool ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const;
+
// Dumps all the loaded profile info into a string and returns it.
// If dex_files is not null then the method indices will be resolved to their
// names.