Implementation of fast lookup table to search class_def by descriptor
Lookup table is a hash table which built at compile time and stored
into oat file. At runtime the table is restored and used in the
method DexFile::FindClassDef(const char*) to perform fast search of
the class_def_idx by class descriptor. Advantages of the lookup table
over the HashSet (runtime/base/hash_set.h) are:
1. Lookup table is built at compile time and uses read-only memory at
runtime
2. Lookup table uses less memory then DexFile::Index (less by 80% for
/system/framework/framework.jar on Nexus5)
3. Lookup table does less string comparisons compared with HashSet
(less by 70% for zygote process on Nexus5)
The disadvantage of the lookup table is it increased boot.oat size by
0.2% on Nexus5 and application .oat file by 0.3% in average on Nexus5.
mathieuc changes:
Create lookup table in dex2oat to speed up compilation. Clean up code
to follow style guide and use less static functions. Added
performance measurements.
Compile ~100 APKs 5 times with filter interpret-only:
Before:
real 1m8.989s
user 0m59.318s
sys 0m7.773s
After:
real 1m1.493s
user 0m52.055s
sys 0m7.581s
App launch (AOSP N5 maps, average of 45 runs):
Before: 966.84ms
After: 923.733ms
Launch speedup is 4.7%
Memory usage compared to HashSet index on 50 various APK:
32 bit: HashSet ~625694b vs TypeLookupTable ~404268b
64 bit: HashSet ~1251390b vs TypeLookupTable ~404268b
Bug: 10921004
Bug: 20269715
Change-Id: I7246c1d9ad9fe81fe5c5907a4bf70396d8f9242a
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index ae62e2b..b3ca6ac 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -37,6 +37,7 @@
#include "dex_file-inl.h"
#include "dex_file_verifier.h"
#include "globals.h"
+#include "handle_scope-inl.h"
#include "leb128.h"
#include "mirror/field.h"
#include "mirror/method.h"
@@ -44,8 +45,8 @@
#include "os.h"
#include "reflection.h"
#include "safe_map.h"
-#include "handle_scope-inl.h"
#include "thread.h"
+#include "type_lookup_table.h"
#include "utf-inl.h"
#include "utils.h"
#include "well_known_classes.h"
@@ -414,11 +415,19 @@
method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
- find_class_def_misses_(0),
- class_def_index_(nullptr),
oat_dex_file_(oat_dex_file) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
+ const uint8_t* lookup_data = (oat_dex_file != nullptr)
+ ? oat_dex_file->GetLookupTableData()
+ : nullptr;
+ if (lookup_data != nullptr) {
+ if (lookup_data + TypeLookupTable::RawDataLength(*this) > oat_dex_file->GetOatFile()->End()) {
+ LOG(WARNING) << "found truncated lookup table in " << GetLocation();
+ } else {
+ lookup_table_.reset(TypeLookupTable::Open(lookup_data, *this));
+ }
+ }
}
DexFile::~DexFile() {
@@ -426,8 +435,6 @@
// that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
// re-attach, but cleaning up these global references is not obviously useful. It's not as if
// the global reference table is otherwise empty!
- // Remove the index if one were created.
- delete class_def_index_.LoadRelaxed();
}
bool DexFile::Init(std::string* error_msg) {
@@ -477,51 +484,26 @@
const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor, size_t hash) const {
DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash);
- // If we have an index lookup the descriptor via that as its constant time to search.
- Index* index = class_def_index_.LoadSequentiallyConsistent();
- if (index != nullptr) {
- auto it = index->FindWithHash(descriptor, hash);
- return (it == index->end()) ? nullptr : it->second;
+ if (LIKELY(lookup_table_ != nullptr)) {
+ const uint32_t class_def_idx = lookup_table_->Lookup(descriptor, hash);
+ return (class_def_idx != DexFile::kDexNoIndex) ? &GetClassDef(class_def_idx) : nullptr;
}
+
// Fast path for rate no class defs case.
- uint32_t num_class_defs = NumClassDefs();
+ const uint32_t num_class_defs = NumClassDefs();
if (num_class_defs == 0) {
return nullptr;
}
- // Search for class def with 2 binary searches and then a linear search.
- const StringId* string_id = FindStringId(descriptor);
- if (string_id != nullptr) {
- const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
- if (type_id != nullptr) {
- uint16_t type_idx = GetIndexForTypeId(*type_id);
- for (size_t i = 0; i < num_class_defs; ++i) {
- const ClassDef& class_def = GetClassDef(i);
- if (class_def.class_idx_ == type_idx) {
- return &class_def;
- }
+ const TypeId* type_id = FindTypeId(descriptor);
+ if (type_id != nullptr) {
+ uint16_t type_idx = GetIndexForTypeId(*type_id);
+ for (size_t i = 0; i < num_class_defs; ++i) {
+ const ClassDef& class_def = GetClassDef(i);
+ if (class_def.class_idx_ == type_idx) {
+ return &class_def;
}
}
}
- // A miss. If we've had kMaxFailedDexClassDefLookups misses then build an index to speed things
- // up. This isn't done eagerly at construction as construction is not performed in multi-threaded
- // sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing
- // out which thread builds the index.
- const uint32_t kMaxFailedDexClassDefLookups = 100;
- uint32_t old_misses = find_class_def_misses_.FetchAndAddSequentiallyConsistent(1);
- if (old_misses == kMaxFailedDexClassDefLookups) {
- // Are we the ones moving the miss count past the max? Sanity check the index doesn't exist.
- CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
- // Build the index.
- index = new Index();
- for (uint32_t i = 0; i < num_class_defs; ++i) {
- const ClassDef& class_def = GetClassDef(i);
- const char* class_descriptor = GetClassDescriptor(class_def);
- index->Insert(std::make_pair(class_descriptor, &class_def));
- }
- // Sanity check the index still doesn't exist, only 1 thread should build it.
- CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
- class_def_index_.StoreSequentiallyConsistent(index);
- }
return nullptr;
}
@@ -625,6 +607,26 @@
return nullptr;
}
+const DexFile::TypeId* DexFile::FindTypeId(const char* string) const {
+ int32_t lo = 0;
+ int32_t hi = NumTypeIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const TypeId& type_id = GetTypeId(mid);
+ const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_);
+ const char* str = GetStringData(str_id);
+ int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
+ if (compare > 0) {
+ lo = mid + 1;
+ } else if (compare < 0) {
+ hi = mid - 1;
+ } else {
+ return &type_id;
+ }
+ }
+ return nullptr;
+}
+
const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const {
int32_t lo = 0;
int32_t hi = NumStringIds() - 1;
@@ -697,6 +699,10 @@
return nullptr;
}
+void DexFile::CreateTypeLookupTable() const {
+ lookup_table_.reset(TypeLookupTable::Create(*this));
+}
+
// Given a signature place the type ids into the given vector
bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
std::vector<uint16_t>* param_type_idxs) const {