Faster Class::FindClassMethod().
Avoid using `strlen()` on ASCII strings from DexFile.
This was a missed opportunity in
https://android-review.googlesource.com/963405 .
Also optimize iteration over declared methods of a class.
Load the dex file before the loop and avoid the runtime
method check in `ArtMethod::GetMethodNameView()` by using
the `DexFile` interface directly.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Change-Id: I1ce3659b7f1fbcfc11d52626f9feb9be666d1161
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index 0e1fb68..c85751a 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -44,6 +44,7 @@
return DecodeUnsignedLeb128(&ptr);
}
+ALWAYS_INLINE
inline const char* DexFile::GetStringDataAndUtf16Length(const dex::StringId& string_id,
uint32_t* utf16_length) const {
DCHECK(utf16_length != nullptr) << GetLocation();
@@ -52,11 +53,13 @@
return reinterpret_cast<const char*>(ptr);
}
+ALWAYS_INLINE
inline const char* DexFile::GetStringData(const dex::StringId& string_id) const {
uint32_t ignored;
return GetStringDataAndUtf16Length(string_id, &ignored);
}
+ALWAYS_INLINE
inline const char* DexFile::StringDataAndUtf16LengthByIdx(dex::StringIndex idx,
uint32_t* utf16_length) const {
if (!idx.IsValid()) {
@@ -67,11 +70,13 @@
return GetStringDataAndUtf16Length(string_id, utf16_length);
}
+ALWAYS_INLINE
inline const char* DexFile::StringDataByIdx(dex::StringIndex idx) const {
uint32_t unicode_length;
return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
}
+ALWAYS_INLINE
inline std::string_view DexFile::StringViewByIdx(dex::StringIndex idx) const {
uint32_t unicode_length;
const char* data = StringDataAndUtf16LengthByIdx(idx, &unicode_length);
@@ -138,6 +143,16 @@
return StringDataAndUtf16LengthByIdx(GetMethodId(idx).name_idx_, utf_length);
}
+ALWAYS_INLINE
+inline std::string_view DexFile::GetMethodNameView(const dex::MethodId& method_id) const {
+ return StringViewByIdx(method_id.name_idx_);
+}
+
+ALWAYS_INLINE
+inline std::string_view DexFile::GetMethodNameView(uint32_t idx) const {
+ return GetMethodNameView(GetMethodId(idx));
+}
+
inline const char* DexFile::GetMethodShorty(uint32_t idx) const {
return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_);
}
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 5363b00..ca5e73b 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -404,6 +404,8 @@
const char* GetMethodName(const dex::MethodId& method_id, uint32_t* utf_length) const;
const char* GetMethodName(uint32_t idx) const;
const char* GetMethodName(uint32_t idx, uint32_t* utf_length) const;
+ std::string_view GetMethodNameView(const dex::MethodId& method_id) const;
+ std::string_view GetMethodNameView(uint32_t idx) const;
// Returns the shorty of a method by its index.
const char* GetMethodShorty(uint32_t idx) const;
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 630a8a5..8dede3a 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -209,9 +209,7 @@
if (LIKELY(dex_method_idx != dex::kDexNoIndex)) {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
- uint32_t length = 0;
- const char* name = dex_file->GetMethodName(dex_file->GetMethodId(dex_method_idx), &length);
- return StringViewFromUtf16Length(name, length);
+ return dex_file->GetMethodNameView(dex_method_idx);
}
return GetRuntimeMethodName();
}
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 3f9d41c..8064bcc 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -634,7 +634,7 @@
// Search declared methods first.
for (ArtMethod& method : this_klass->GetDeclaredMethodsSlice(pointer_size)) {
ArtMethod* np_method = method.GetInterfaceMethodIfProxy(pointer_size);
- if (np_method->GetName() == name && np_method->GetSignature() == signature) {
+ if (np_method->GetNameView() == name && np_method->GetSignature() == signature) {
return &method;
}
}
@@ -647,7 +647,7 @@
for (; klass != nullptr; klass = klass->GetSuperClass()) {
DCHECK(!klass->IsProxyClass());
for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) {
- if (method.GetName() == name && method.GetSignature() == signature) {
+ if (method.GetNameView() == name && method.GetSignature() == signature) {
if (IsInheritedMethod(this_klass, klass, method)) {
return &method;
}
@@ -672,7 +672,7 @@
for (; klass != end_klass; klass = klass->GetSuperClass()) {
DCHECK(!klass->IsProxyClass());
for (ArtMethod& method : klass->GetCopiedMethodsSlice(pointer_size)) {
- if (method.GetName() == name && method.GetSignature() == signature) {
+ if (method.GetNameView() == name && method.GetSignature() == signature) {
return &method; // No further check needed, copied methods are inherited by definition.
}
}
@@ -693,6 +693,7 @@
return FindClassMethodWithSignature(this, name, signature, pointer_size);
}
+FLATTEN
ArtMethod* Class::FindClassMethod(ObjPtr<DexCache> dex_cache,
uint32_t dex_method_idx,
PointerSize pointer_size) {
@@ -714,18 +715,19 @@
const DexFile& dex_file = *dex_cache->GetDexFile();
const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const Signature signature = dex_file.GetMethodSignature(method_id);
- std::string_view name; // Delay strlen() until actually needed.
+ std::string_view name; // Do not touch the dex file string data until actually needed.
// If we do not have a dex_cache match, try to find the declared method in this class now.
if (this_dex_cache != dex_cache && !GetDeclaredMethodsSlice(pointer_size).empty()) {
DCHECK(name.empty());
- // Avoid string comparisons by comparing the respective unicode lengths first.
- uint32_t length, other_length; // UTF16 length.
- name = dex_file.GetMethodName(method_id, &length);
+ name = dex_file.GetMethodNameView(method_id);
+ const DexFile& this_dex_file = *this_dex_cache->GetDexFile();
for (ArtMethod& method : GetDeclaredMethodsSlice(pointer_size)) {
+ // Do not use ArtMethod::GetNameView() to avoid reloading dex file through the same
+ // declaring class from different methods and also avoid the runtime method check.
+ DCHECK(method.GetDexFile() == &this_dex_file);
DCHECK_NE(method.GetDexMethodIndex(), dex::kDexNoIndex);
- const char* other_name = method.GetDexFile()->GetMethodName(
- method.GetDexMethodIndex(), &other_length);
- if (length == other_length && name == other_name && signature == method.GetSignature()) {
+ std::string_view other_name = this_dex_file.GetMethodNameView(method.GetDexMethodIndex());
+ if (other_name == name && method.GetSignature() == signature) {
return &method;
}
}
@@ -750,12 +752,18 @@
break;
}
}
- } else {
- if (!declared_methods.empty() && name.empty()) {
- name = dex_file.StringDataByIdx(method_id.name_idx_);
+ } else if (!declared_methods.empty()) {
+ if (name.empty()) {
+ name = dex_file.GetMethodNameView(method_id);
}
+ const DexFile& other_dex_file = klass->GetDexFile();
for (ArtMethod& method : declared_methods) {
- if (method.GetName() == name && method.GetSignature() == signature) {
+ // Do not use ArtMethod::GetNameView() to avoid reloading dex file through the same
+ // declaring class from different methods and also avoid the runtime method check.
+ DCHECK(method.GetDexFile() == &other_dex_file);
+ DCHECK_NE(method.GetDexMethodIndex(), dex::kDexNoIndex);
+ std::string_view other_name = other_dex_file.GetMethodNameView(method.GetDexMethodIndex());
+ if (other_name == name && method.GetSignature() == signature) {
candidate_method = &method;
break;
}
@@ -783,7 +791,7 @@
name = dex_file.StringDataByIdx(method_id.name_idx_);
}
for (ArtMethod& method : copied_methods) {
- if (method.GetName() == name && method.GetSignature() == signature) {
+ if (method.GetNameView() == name && method.GetSignature() == signature) {
return &method; // No further check needed, copied methods are inherited by definition.
}
}