Move compiled methods to use AtomicMethodRefMap
Use AtomicMethodRefMap to avoid locking and map costs.
Native RAM usage for interpret-only compile FB:
71270192B -> 64117168B
Perf:
CompilerDriver::GetCompiledMethod: 0.88% -> 0.04%
Mutex::ExclusiveLock: 1.55% -> 1.05%
Bug: 32641252
Test: test-art-host-run-test
Change-Id: I146e7bf2f3328b4fa84f2cc19d591f4da82dccfc
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 42e5db3..6b62110 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -71,6 +71,7 @@
#include "thread_pool.h"
#include "trampolines/trampoline_compiler.h"
#include "transaction.h"
+#include "utils/atomic_method_ref_map-inl.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "utils/swap_space.h"
#include "vdex_file.h"
@@ -287,8 +288,6 @@
instruction_set_features_(instruction_set_features),
requires_constructor_barrier_lock_("constructor barrier lock"),
compiled_classes_lock_("compiled classes lock"),
- compiled_methods_lock_("compiled method lock"),
- compiled_methods_(MethodTable::key_compare()),
non_relative_linker_patch_count_(0u),
image_classes_(image_classes),
classes_to_compile_(compiled_classes),
@@ -326,12 +325,12 @@
MutexLock mu(self, compiled_classes_lock_);
STLDeleteValues(&compiled_classes_);
}
- {
- MutexLock mu(self, compiled_methods_lock_);
- for (auto& pair : compiled_methods_) {
- CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, pair.second);
+ compiled_methods_.Visit([this](const MethodReference& ref ATTRIBUTE_UNUSED,
+ CompiledMethod* method) {
+ if (method != nullptr) {
+ CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, method);
}
- }
+ });
compiler_->UnInit();
}
@@ -575,8 +574,7 @@
const DexFile& dex_file,
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level,
bool compilation_enabled,
- Handle<mirror::DexCache> dex_cache)
- REQUIRES(!driver->compiled_methods_lock_) {
+ Handle<mirror::DexCache> dex_cache) {
DCHECK(driver != nullptr);
CompiledMethod* compiled_method = nullptr;
uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
@@ -940,6 +938,13 @@
TimingLogger* timings) {
CheckThreadPools();
+ for (const DexFile* dex_file : dex_files) {
+ // Can be already inserted if the caller is CompileOne. This happens for gtests.
+ if (!compiled_methods_.HaveDexFile(dex_file)) {
+ compiled_methods_.AddDexFile(dex_file);
+ }
+ }
+
LoadImageClasses(timings);
VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
@@ -2616,30 +2621,15 @@
size_t non_relative_linker_patch_count) {
DCHECK(GetCompiledMethod(method_ref) == nullptr)
<< method_ref.dex_file->PrettyMethod(method_ref.dex_method_index);
- {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- compiled_methods_.Put(method_ref, compiled_method);
- non_relative_linker_patch_count_ += non_relative_linker_patch_count;
- }
+ MethodTable::InsertResult result = compiled_methods_.Insert(method_ref,
+ /*expected*/ nullptr,
+ compiled_method);
+ CHECK(result == MethodTable::kInsertResultSuccess);
+ non_relative_linker_patch_count_.FetchAndAddRelaxed(non_relative_linker_patch_count);
DCHECK(GetCompiledMethod(method_ref) != nullptr)
<< method_ref.dex_file->PrettyMethod(method_ref.dex_method_index);
}
-void CompilerDriver::RemoveCompiledMethod(const MethodReference& method_ref) {
- CompiledMethod* compiled_method = nullptr;
- {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- auto it = compiled_methods_.find(method_ref);
- if (it != compiled_methods_.end()) {
- compiled_method = it->second;
- compiled_methods_.erase(it);
- }
- }
- if (compiled_method != nullptr) {
- CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, compiled_method);
- }
-}
-
CompiledClass* CompilerDriver::GetCompiledClass(ClassReference ref) const {
MutexLock mu(Thread::Current(), compiled_classes_lock_);
ClassTable::const_iterator it = compiled_classes_.find(ref);
@@ -2678,13 +2668,9 @@
}
CompiledMethod* CompilerDriver::GetCompiledMethod(MethodReference ref) const {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- MethodTable::const_iterator it = compiled_methods_.find(ref);
- if (it == compiled_methods_.end()) {
- return nullptr;
- }
- CHECK(it->second != nullptr);
- return it->second;
+ CompiledMethod* compiled_method = nullptr;
+ compiled_methods_.Get(ref, &compiled_method);
+ return compiled_method;
}
bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
@@ -2713,8 +2699,7 @@
}
size_t CompilerDriver::GetNonRelativeLinkerPatchCount() const {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- return non_relative_linker_patch_count_;
+ return non_relative_linker_patch_count_.LoadRelaxed();
}
void CompilerDriver::SetRequiresConstructorBarrier(Thread* self,
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 7418b00..cc50197 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -41,6 +41,7 @@
#include "runtime.h"
#include "safe_map.h"
#include "thread_pool.h"
+#include "utils/atomic_method_ref_map.h"
#include "utils/dex_cache_arrays_layout.h"
namespace art {
@@ -131,7 +132,7 @@
// Compile a single Method.
void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings)
REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
+ REQUIRES(!compiled_classes_lock_, !dex_to_dex_references_lock_);
VerificationResults* GetVerificationResults() const {
DCHECK(Runtime::Current()->IsAotCompiler());
@@ -168,18 +169,12 @@
CompiledClass* GetCompiledClass(ClassReference ref) const
REQUIRES(!compiled_classes_lock_);
- CompiledMethod* GetCompiledMethod(MethodReference ref) const
- REQUIRES(!compiled_methods_lock_);
- size_t GetNonRelativeLinkerPatchCount() const
- REQUIRES(!compiled_methods_lock_);
-
+ CompiledMethod* GetCompiledMethod(MethodReference ref) const;
+ size_t GetNonRelativeLinkerPatchCount() const;
// Add a compiled method.
void AddCompiledMethod(const MethodReference& method_ref,
CompiledMethod* const compiled_method,
- size_t non_relative_linker_patch_count)
- REQUIRES(!compiled_methods_lock_);
- // Remove and delete a compiled method.
- void RemoveCompiledMethod(const MethodReference& method_ref) REQUIRES(!compiled_methods_lock_);
+ size_t non_relative_linker_patch_count);
void SetRequiresConstructorBarrier(Thread* self,
const DexFile* dex_file,
@@ -519,18 +514,15 @@
mutable Mutex compiled_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ClassTable compiled_classes_ GUARDED_BY(compiled_classes_lock_);
- typedef SafeMap<const MethodReference, CompiledMethod*, MethodReferenceComparator> MethodTable;
-
- public:
- // Lock is public so that non-members can have lock annotations.
- mutable Mutex compiled_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ typedef AtomicMethodRefMap<CompiledMethod*> MethodTable;
private:
// All method references that this compiler has compiled.
- MethodTable compiled_methods_ GUARDED_BY(compiled_methods_lock_);
+ MethodTable compiled_methods_;
+
// Number of non-relative patches in all compiled methods. These patches need space
// in the .oat_patches ELF section if requested in the compiler options.
- size_t non_relative_linker_patch_count_ GUARDED_BY(compiled_methods_lock_);
+ Atomic<size_t> non_relative_linker_patch_count_;
// If image_ is true, specifies the classes that will be included in the image.
// Note if image_classes_ is null, all classes are included in the image.