diff options
author | 2024-04-03 15:00:46 +0000 | |
---|---|---|
committer | 2024-04-05 12:51:04 +0000 | |
commit | 4e6db6efb36ab9cb9a5990346e77d7976e1b3c66 (patch) | |
tree | fdbee9887db4f972908d34ae6160e1563ffed0bb | |
parent | 9486862224c0c3b99813b9a249c4af97452e505f (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
-rw-r--r-- | libdexfile/dex/dex_file.h | 14 | ||||
-rw-r--r-- | runtime/class_linker.cc | 47 | ||||
-rw-r--r-- | runtime/class_linker.h | 4 | ||||
-rw-r--r-- | runtime/dex/dex_file_annotations.cc | 51 | ||||
-rw-r--r-- | runtime/dex/dex_file_annotations.h | 6 |
5 files changed, 95 insertions, 27 deletions
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 8e399af812..4186a80b43 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -704,12 +704,14 @@ class DexFile { const dex::AnnotationSetItem* GetFieldAnnotationSetItem( const dex::FieldAnnotationsItem& anno_item) const { - return DataPointer<dex::AnnotationSetItem>(anno_item.annotations_off_); + // `DexFileVerifier` checks that the offset is not zero. + return NonNullDataPointer<dex::AnnotationSetItem>(anno_item.annotations_off_); } const dex::AnnotationSetItem* GetMethodAnnotationSetItem( const dex::MethodAnnotationsItem& anno_item) const { - return DataPointer<dex::AnnotationSetItem>(anno_item.annotations_off_); + // `DexFileVerifier` checks that the offset is not zero. + return NonNullDataPointer<dex::AnnotationSetItem>(anno_item.annotations_off_); } const dex::AnnotationSetRefList* GetParameterAnnotationSetRefList( @@ -821,8 +823,14 @@ class DexFile { template <typename T> const T* DataPointer(size_t offset) const { + return (offset != 0u) ? NonNullDataPointer<T>(offset) : nullptr; + } + + template <typename T> + const T* NonNullDataPointer(size_t offset) const { + DCHECK_NE(offset, 0u); DCHECK_LT(offset, DataSize()) << "Offset past end of data section"; - return (offset != 0u) ? reinterpret_cast<const T*>(DataBegin() + offset) : nullptr; + return reinterpret_cast<const T*>(DataBegin() + offset); } const OatDexFile* GetOatDexFile() const { 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); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f964a5573c..edb866adc7 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -924,6 +924,7 @@ class EXPORT ClassLinker { class LinkFieldsHelper; template <PointerSize kPointerSize> class LinkMethodsHelper; + class MethodAnnotationsIterator; class VisiblyInitializedCallback; struct ClassLoaderData { @@ -1043,7 +1044,8 @@ class EXPORT ClassLinker { void LoadMethod(const DexFile& dex_file, const ClassAccessor::Method& method, ObjPtr<mirror::Class> klass, - ArtMethod* dst) + /*inout*/ MethodAnnotationsIterator* mai, + /*out*/ ArtMethod* dst) REQUIRES_SHARED(Locks::mutator_lock_); void FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> klass) diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index ab2384ee95..8488993ae4 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -1295,25 +1295,19 @@ static bool IsMethodBuildAnnotationPresent(const DexFile& dex_file, return false; } -uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, - const dex::ClassDef& class_def, - uint32_t method_index) { - const dex::AnnotationSetItem* annotation_set = - FindAnnotationSetForMethod(dex_file, class_def, method_index); - if (annotation_set == nullptr) { - return 0u; - } +static uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const dex::AnnotationSetItem& annotation_set) { uint32_t access_flags = 0u; if (IsMethodBuildAnnotationPresent( dex_file, - *annotation_set, + annotation_set, "Ldalvik/annotation/optimization/FastNative;", WellKnownClasses::dalvik_annotation_optimization_FastNative)) { access_flags |= kAccFastNative; } if (IsMethodBuildAnnotationPresent( dex_file, - *annotation_set, + annotation_set, "Ldalvik/annotation/optimization/CriticalNative;", WellKnownClasses::dalvik_annotation_optimization_CriticalNative)) { access_flags |= kAccCriticalNative; @@ -1322,21 +1316,48 @@ uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, return access_flags; } -bool MethodIsNeverCompile(const DexFile& dex_file, - const dex::ClassDef& class_def, - uint32_t method_index) { +uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const dex::ClassDef& class_def, + uint32_t method_index) { const dex::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(dex_file, class_def, method_index); if (annotation_set == nullptr) { - return false; + return 0u; } + return GetNativeMethodAnnotationAccessFlags(dex_file, *annotation_set); +} + +uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const dex::MethodAnnotationsItem& method_annotations) { + return GetNativeMethodAnnotationAccessFlags( + dex_file, *dex_file.GetMethodAnnotationSetItem(method_annotations)); +} + +static bool MethodIsNeverCompile(const DexFile& dex_file, + const dex::AnnotationSetItem& annotation_set) { return IsMethodBuildAnnotationPresent( dex_file, - *annotation_set, + annotation_set, "Ldalvik/annotation/optimization/NeverCompile;", WellKnownClasses::dalvik_annotation_optimization_NeverCompile); } +bool MethodIsNeverCompile(const DexFile& dex_file, + const dex::ClassDef& class_def, + uint32_t method_index) { + const dex::AnnotationSetItem* annotation_set = + FindAnnotationSetForMethod(dex_file, class_def, method_index); + if (annotation_set == nullptr) { + return false; + } + return MethodIsNeverCompile(dex_file, *annotation_set); +} + +bool MethodIsNeverCompile(const DexFile& dex_file, + const dex::MethodAnnotationsItem& method_annotations) { + return MethodIsNeverCompile(dex_file, *dex_file.GetMethodAnnotationSetItem(method_annotations)); +} + bool MethodIsNeverInline(const DexFile& dex_file, const dex::ClassDef& class_def, uint32_t method_index) { diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index 3a79e11256..6f6c6ddc1f 100644 --- a/runtime/dex/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -86,11 +86,17 @@ bool IsMethodAnnotationPresent(ArtMethod* method, EXPORT uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, const dex::ClassDef& class_def, uint32_t method_index); +// An overload of `GetNativeMethodAnnotationAccessFlags()` that takes a `MethodAnnotationsItem`. +uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const dex::MethodAnnotationsItem& method_annotations); // Is the method from the `dex_file` with the given `field_index` // annotated with @dalvik.annotation.optimization.NeverCompile? EXPORT bool MethodIsNeverCompile(const DexFile& dex_file, const dex::ClassDef& class_def, uint32_t method_index); +// An overload of `MethodIsNeverCompile()` that takes a `MethodAnnotationsItem`. +bool MethodIsNeverCompile(const DexFile& dex_file, + const dex::MethodAnnotationsItem& method_annotations); // Is the method from the `dex_file` with the given `field_index` // annotated with @dalvik.annotation.optimization.NeverInline? bool MethodIsNeverInline(const DexFile& dex_file, |