Use a per-thread VerifierDeps.
Avoid lock contention on a singleton VerifierDeps by allocating
temporary per-thread VerifierDeps that get merged after verification.
This saves around ~35% compile-times on interpret-only.
Only the creation of extra strings is guarded by a lock, for simplicity.
Test: test-art-host, test-art-target
bug: 32641252
bug: 30937355
Change-Id: I11a2367da882b58e39afa7b42cba2e74a209b75d
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c62e214..e155e10 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1980,8 +1980,16 @@
// non boot image compilation. The verifier will need it to record the new dependencies.
// Then dex2oat can update the vdex file with these new dependencies.
if (!GetCompilerOptions().IsBootImage()) {
+ // Create the main VerifierDeps, and set it to this thread.
Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
new verifier::VerifierDeps(dex_files));
+ Thread::Current()->SetVerifierDeps(
+ Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps());
+ // Create per-thread VerifierDeps to avoid contention on the main one.
+ // We will merge them after verification.
+ for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
+ worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files));
+ }
}
// Note: verification should not be pulling in classes anymore when compiling the boot image,
// as all should have been resolved before. As such, doing this in parallel should still
@@ -1995,6 +2003,19 @@
parallel_thread_count_,
timings);
}
+
+ if (!GetCompilerOptions().IsBootImage()) {
+ verifier::VerifierDeps* main_deps =
+ Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+ // Merge all VerifierDeps into the main one.
+ for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
+ verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
+ worker->GetThread()->SetVerifierDeps(nullptr);
+ main_deps->MergeWith(*thread_deps, dex_files);;
+ delete thread_deps;
+ }
+ Thread::Current()->SetVerifierDeps(nullptr);
+ }
}
class VerifyClassVisitor : public CompilationVisitor {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 03d3f4e..dcf3619 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -154,6 +154,7 @@
}
CHECK(method != nullptr);
+ Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps());
MethodVerifier verifier(Thread::Current(),
primary_dex_file_,
dex_cache_handle,
@@ -169,6 +170,7 @@
false /* verify to dump */,
true /* allow_thread_suspension */);
verifier.Verify();
+ Thread::Current()->SetVerifierDeps(nullptr);
return !verifier.HasFailures();
}
@@ -230,7 +232,6 @@
const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str());
DCHECK(type_id != nullptr);
dex::TypeIndex index = primary_dex_file_->GetIndexForTypeId(*type_id);
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
for (const auto& dex_dep : verifier_deps_->dex_deps_) {
for (dex::TypeIndex entry : dex_dep.second->unverified_classes_) {
if (index == entry) {
@@ -246,7 +247,6 @@
bool HasAssignable(const std::string& expected_destination,
const std::string& expected_source,
bool expected_is_assignable) {
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
for (auto& dex_dep : verifier_deps_->dex_deps_) {
const DexFile& dex_file = *dex_dep.first;
auto& storage = expected_is_assignable ? dex_dep.second->assignable_types_
@@ -268,7 +268,6 @@
bool HasClass(const std::string& expected_klass,
bool expected_resolved,
const std::string& expected_access_flags = "") {
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
for (auto& dex_dep : verifier_deps_->dex_deps_) {
for (auto& entry : dex_dep.second->classes_) {
if (expected_resolved != entry.IsResolved()) {
@@ -303,7 +302,6 @@
bool expected_resolved,
const std::string& expected_access_flags = "",
const std::string& expected_decl_klass = "") {
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
for (auto& dex_dep : verifier_deps_->dex_deps_) {
for (auto& entry : dex_dep.second->fields_) {
if (expected_resolved != entry.IsResolved()) {
@@ -357,7 +355,6 @@
bool expected_resolved,
const std::string& expected_access_flags = "",
const std::string& expected_decl_klass = "") {
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
for (auto& dex_dep : verifier_deps_->dex_deps_) {
auto& storage = (expected_kind == "direct") ? dex_dep.second->direct_methods_
: (expected_kind == "virtual") ? dex_dep.second->virtual_methods_
@@ -406,13 +403,10 @@
}
size_t NumberOfCompiledDexFiles() {
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
return verifier_deps_->dex_deps_.size();
}
size_t HasEachKindOfRecord() {
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
bool has_strings = false;
bool has_assignability = false;
bool has_classes = false;
@@ -463,8 +457,6 @@
ScopedObjectAccess soa(Thread::Current());
LoadDexFile(&soa);
- MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;");
ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds());
ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1));