summaryrefslogtreecommitdiff
path: root/runtime/class_linker.cc
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2024-04-03 15:00:46 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2024-04-05 12:51:04 +0000
commit4e6db6efb36ab9cb9a5990346e77d7976e1b3c66 (patch)
treefdbee9887db4f972908d34ae6160e1563ffed0bb /runtime/class_linker.cc
parent9486862224c0c3b99813b9a249c4af97452e505f (diff)
Faster annotations processing in `ClassLinker::LoadClass()`.
Iterate annotations alongside other method data, using the fact that both are ordered by method index. We use separate iterators for loading direct and virtual methods. For a test class with 500 unannotated static methods and 200 @NeverCompile static methods, this change reduces the time needed by `ClassLinker::LoadClass` approximately 2x. For a test class with twice that many methods, the time without this change increases by ~3x but with this change the time increases by ~2x, showing nice linear scaling. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 329196666 Change-Id: I800b8e8e9f9e5a4fbfbda1361f99feb8c43ff1d5
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r--runtime/class_linker.cc47
1 files changed, 39 insertions, 8 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9d405207a5..9f463ada75 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3868,6 +3868,29 @@ LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(ObjPtr<mirror::Clas
return allocator;
}
+// Helper class for iterating over method annotations, using their ordering in the dex file.
+// Since direct and virtual methods are separated (but each section is ordered), we shall use
+// separate iterators for loading direct and virtual methods.
+class ClassLinker::MethodAnnotationsIterator {
+ public:
+ MethodAnnotationsIterator(const DexFile& dex_file,
+ const dex::AnnotationsDirectoryItem* annotations_dir)
+ : current_((annotations_dir != nullptr) ? dex_file.GetMethodAnnotations(annotations_dir)
+ : nullptr),
+ end_((annotations_dir != nullptr) ? current_ + annotations_dir->methods_size_ : nullptr) {}
+
+ const dex::MethodAnnotationsItem* AdvanceTo(uint32_t method_idx) {
+ while (current_ != end_ && current_->method_idx_ < method_idx) {
+ ++current_;
+ }
+ return (current_ != end_ && current_->method_idx_ == method_idx) ? current_ : nullptr;
+ }
+
+ private:
+ const dex::MethodAnnotationsItem* current_;
+ const dex::MethodAnnotationsItem* const end_;
+};
+
void ClassLinker::LoadClass(Thread* self,
const DexFile& dex_file,
const dex::ClassDef& dex_class_def,
@@ -3912,6 +3935,10 @@ void ClassLinker::LoadClass(Thread* self,
uint32_t last_dex_method_index = dex::kDexNoIndex;
size_t last_class_def_method_index = 0;
+ // Initialize separate `MethodAnnotationsIterator`s for direct and virtual methods.
+ MethodAnnotationsIterator mai_direct(dex_file, dex_file.GetAnnotationsDirectory(dex_class_def));
+ MethodAnnotationsIterator mai_virtual = mai_direct;
+
uint16_t hotness_threshold = runtime->GetJITOptions()->GetWarmupThreshold();
// Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the
// methods needs to decode all of the fields.
@@ -3935,7 +3962,7 @@ void ClassLinker::LoadClass(Thread* self,
}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index,
image_pointer_size_);
- LoadMethod(dex_file, method, klass.Get(), art_method);
+ LoadMethod(dex_file, method, klass.Get(), &mai_direct, art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
uint32_t it_method_index = method.GetIndex();
if (last_dex_method_index == it_method_index) {
@@ -3953,7 +3980,7 @@ void ClassLinker::LoadClass(Thread* self,
class_def_method_index - accessor.NumDirectMethods(),
image_pointer_size_);
art_method->ResetCounter(hotness_threshold);
- LoadMethod(dex_file, method, klass.Get(), art_method);
+ LoadMethod(dex_file, method, klass.Get(), &mai_virtual, art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
++class_def_method_index;
});
@@ -3996,7 +4023,8 @@ void ClassLinker::LoadField(const ClassAccessor::Field& field,
void ClassLinker::LoadMethod(const DexFile& dex_file,
const ClassAccessor::Method& method,
ObjPtr<mirror::Class> klass,
- ArtMethod* dst) {
+ /*inout*/ MethodAnnotationsIterator* mai,
+ /*out*/ ArtMethod* dst) {
ScopedAssertNoThreadSuspension sants(__FUNCTION__);
const uint32_t dex_method_idx = method.GetIndex();
@@ -4045,9 +4073,11 @@ void ClassLinker::LoadMethod(const DexFile& dex_file,
if (UNLIKELY((access_flags & kAccNative) != 0u)) {
// Check if the native method is annotated with @FastNative or @CriticalNative.
- const dex::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
- access_flags |=
- annotations::GetNativeMethodAnnotationAccessFlags(dex_file, class_def, dex_method_idx);
+ const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
+ if (method_annotations != nullptr) {
+ access_flags |=
+ annotations::GetNativeMethodAnnotationAccessFlags(dex_file, *method_annotations);
+ }
dst->SetAccessFlags(access_flags);
DCHECK(!dst->IsAbstract());
DCHECK(!dst->HasCodeItem());
@@ -4064,8 +4094,9 @@ void ClassLinker::LoadMethod(const DexFile& dex_file,
DCHECK_EQ(method.GetCodeItemOffset(), 0u);
dst->SetDataPtrSize(nullptr, image_pointer_size_); // Single implementation not set yet.
} else {
- const dex::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
- if (annotations::MethodIsNeverCompile(dex_file, class_def, dex_method_idx)) {
+ const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
+ if (method_annotations != nullptr &&
+ annotations::MethodIsNeverCompile(dex_file, *method_annotations)) {
access_flags |= kAccCompileDontBother;
}
dst->SetAccessFlags(access_flags);