Reduce calls to DescriptorEquals
Store the low 3 bits of the descriptor hash inside of class set
entries. Compare these bits before comparing descriptors.
Simpleperf interpret-only compile of facebook:
mirror::Class::DescriptorEquals(char const*): 3.66% -> 1.03%
Bug: 32641252
Test: test-art-host
Change-Id: I8d898d4ac7c95383c49401fbcd85bfde226e026c
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 558c144..fe0bbb3 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -42,33 +42,91 @@
// Each loader has a ClassTable
class ClassTable {
public:
+ class TableSlot {
+ public:
+ TableSlot() : data_(0u) {}
+
+ TableSlot(const TableSlot& copy) : data_(copy.data_.LoadRelaxed()) {}
+
+ explicit TableSlot(ObjPtr<mirror::Class> klass);
+
+ TableSlot(ObjPtr<mirror::Class> klass, uint32_t descriptor_hash);
+
+ TableSlot& operator=(const TableSlot& copy) {
+ data_.StoreRelaxed(copy.data_.LoadRelaxed());
+ return *this;
+ }
+
+ bool IsNull() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return Read<kWithoutReadBarrier>() == nullptr;
+ }
+
+ uint32_t Hash() const {
+ return MaskHash(data_.LoadRelaxed());
+ }
+
+ static uint32_t MaskHash(uint32_t hash) {
+ return hash & kHashMask;
+ }
+
+ bool MaskedHashEquals(uint32_t other) const {
+ return MaskHash(other) == Hash();
+ }
+
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ mirror::Class* Read() const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // NO_THREAD_SAFETY_ANALYSIS since the visitor may require heap bitmap lock.
+ template<typename Visitor>
+ void VisitRoot(const Visitor& visitor) const NO_THREAD_SAFETY_ANALYSIS;
+
+ private:
+ // Extract a raw pointer from an address.
+ static ObjPtr<mirror::Class> ExtractPtr(uint32_t data)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static uint32_t Encode(ObjPtr<mirror::Class> klass, uint32_t hash_bits)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Data contains the class pointer GcRoot as well as the low bits of the descriptor hash.
+ mutable Atomic<uint32_t> data_;
+ static const uint32_t kHashMask = kObjectAlignment - 1;
+ };
+
+ using DescriptorHashPair = std::pair<const char*, uint32_t>;
+
class ClassDescriptorHashEquals {
public:
// uint32_t for cross compilation.
- uint32_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS;
+ uint32_t operator()(const TableSlot& slot) const NO_THREAD_SAFETY_ANALYSIS;
// Same class loader and descriptor.
- bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const
+ bool operator()(const TableSlot& a, const TableSlot& b) const
NO_THREAD_SAFETY_ANALYSIS;
// Same descriptor.
- bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor) const
+ bool operator()(const TableSlot& a, const DescriptorHashPair& b) const
NO_THREAD_SAFETY_ANALYSIS;
// uint32_t for cross compilation.
- uint32_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS;
+ uint32_t operator()(const DescriptorHashPair& pair) const NO_THREAD_SAFETY_ANALYSIS;
};
- class GcRootEmptyFn {
+
+ class TableSlotEmptyFn {
public:
- void MakeEmpty(GcRoot<mirror::Class>& item) const {
- item = GcRoot<mirror::Class>();
+ void MakeEmpty(TableSlot& item) const NO_THREAD_SAFETY_ANALYSIS {
+ item = TableSlot();
+ DCHECK(IsEmpty(item));
}
- bool IsEmpty(const GcRoot<mirror::Class>& item) const {
+ bool IsEmpty(const TableSlot& item) const NO_THREAD_SAFETY_ANALYSIS {
return item.IsNull();
}
};
- // hash set which hashes class descriptor, and compares descriptors and class loaders. Results
- // should be compared for a matching Class descriptor and class loader.
- typedef HashSet<GcRoot<mirror::Class>, GcRootEmptyFn, ClassDescriptorHashEquals,
- ClassDescriptorHashEquals, TrackingAllocator<GcRoot<mirror::Class>, kAllocatorTagClassTable>>
- ClassSet;
+
+ // Hash set that hashes class descriptor, and compares descriptors and class loaders. Results
+ // should be compared for a matching class descriptor and class loader.
+ typedef HashSet<TableSlot,
+ TableSlotEmptyFn,
+ ClassDescriptorHashEquals,
+ ClassDescriptorHashEquals,
+ TrackingAllocator<TableSlot, kAllocatorTagClassTable>> ClassSet;
ClassTable();