v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
off-heap-hash-table-inl.h
Go to the documentation of this file.
1// Copyright 2023 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_OBJECTS_OFF_HEAP_HASH_TABLE_INL_H_
6#define V8_OBJECTS_OFF_HEAP_HASH_TABLE_INL_H_
7
9// Include the non-inl header before the rest of the headers.
10
12
13namespace v8 {
14namespace internal {
15
16template <typename Derived>
18 : number_of_elements_(0),
19 number_of_deleted_elements_(0),
20 capacity_(capacity) {
22 capacity * Derived::kEntrySize);
23}
24
25template <typename Derived>
27 Derived* new_table) {
28 DCHECK_LT(number_of_elements(), new_table->capacity());
29 DCHECK(new_table->HasSufficientCapacityToAdd(number_of_elements()));
30
31 Derived* derived_this = static_cast<Derived*>(this);
32 // Rehash the elements and copy them into new_table.
33 for (InternalIndex i : InternalIndex::Range(capacity())) {
34 Tagged<Object> key = derived_this->GetKey(cage_base, i);
35 if (!IsKey(key)) continue;
36 uint32_t hash = Derived::Hash(cage_base, key);
37 InternalIndex insertion_index =
38 new_table->FindInsertionEntry(cage_base, hash);
39 new_table->SetKey(insertion_index, key);
40 derived_this->CopyEntryExcludingKeyInto(cage_base, i, new_table,
41 insertion_index);
42 }
43 new_table->number_of_elements_ = number_of_elements();
44}
45
46template <typename Derived>
48 int additional_elements, int* new_capacity) {
49 DCHECK_NOT_NULL(new_capacity);
50 // Grow or shrink table if needed. We first try to shrink the table, if it
51 // is sufficiently empty; otherwise we make sure to grow it so that it has
52 // enough space.
53 int capacity_after_shrinking = ComputeCapacityWithShrink(
54 capacity_, number_of_elements_ + additional_elements);
55
56 if (capacity_after_shrinking < capacity_) {
57 DCHECK(HasSufficientCapacityToAdd(
58 capacity_after_shrinking, number_of_elements_, 0, additional_elements));
59 *new_capacity = capacity_after_shrinking;
60 return true;
61 } else if (!HasSufficientCapacityToAdd(additional_elements)) {
62 *new_capacity = ComputeCapacity(number_of_elements_ + additional_elements);
63 return true;
64 } else {
65 *new_capacity = -1;
66 return false;
67 }
68}
69
70// static
71template <typename Derived>
73 int capacity, int number_of_elements, int number_of_deleted_elements,
74 int number_of_additional_elements) {
75 int nof = number_of_elements + number_of_additional_elements;
76 // Return true if:
77 // 50% is still free after adding number_of_additional_elements elements and
78 // at most 50% of the free elements are deleted elements.
79 if ((nof < capacity) &&
80 ((number_of_deleted_elements <= (capacity - nof) / 2))) {
81 int needed_free = nof / 2;
82 if (nof + needed_free <= capacity) return true;
83 }
84 return false;
85}
86
87// static
88template <typename Derived>
90 // Add 50% slack to make slot collisions sufficiently unlikely.
91 // See matching computation in HasSufficientCapacityToAdd().
92 int raw_capacity = at_least_space_for + (at_least_space_for >> 1);
93 int capacity = base::bits::RoundUpToPowerOfTwo32(raw_capacity);
94 return std::max(capacity, Derived::kMinCapacity);
95}
96
97// static
98template <typename Derived>
100 int current_capacity, int at_least_space_for) {
101 // Only shrink if the table is very empty to avoid performance penalty.
102 DCHECK_GE(current_capacity, Derived::kMinCapacity);
103 if (at_least_space_for > (current_capacity / Derived::kMaxEmptyFactor)) {
104 return current_capacity;
105 }
106
107 // Recalculate the smaller capacity actually needed.
108 int new_capacity = ComputeCapacity(at_least_space_for);
109 DCHECK_GE(new_capacity, at_least_space_for);
110 // Don't go lower than room for {kMinCapacity} elements.
111 if (new_capacity < Derived::kMinCapacity) return current_capacity;
112 return new_capacity;
113}
114
115template <typename Derived>
117 RootVisitor* visitor) {
118 OffHeapObjectSlot first_slot = slot(InternalIndex(0));
119 OffHeapObjectSlot end_slot = slot(InternalIndex(capacity_));
120 visitor->VisitRootPointers(root, nullptr, first_slot, end_slot);
121}
122
123template <typename Derived>
124template <typename IsolateT, typename FindKey>
126 FindKey key,
127 uint32_t hash) const {
128 const Derived* derived_this = static_cast<const Derived*>(this);
129 uint32_t count = 1;
130 for (InternalIndex entry = FirstProbe(hash, capacity_);;
131 entry = NextProbe(entry, count++, capacity_)) {
132 // TODO(leszeks): Consider delaying the decompression until after the
133 // comparisons against empty/deleted.
134 Tagged<Object> element = derived_this->GetKey(isolate, entry);
135 if (element == empty_element()) return InternalIndex::NotFound();
136 if (element == deleted_element()) continue;
137 if (Derived::KeyIsMatch(isolate, key, element)) return entry;
139}
141template <typename Derived>
143 PtrComprCageBase cage_base, uint32_t hash) const {
144 // The derived class must guarantee the hash table is never full.
145 DCHECK(HasSufficientCapacityToAdd(1));
146 const Derived* derived_this = static_cast<const Derived*>(this);
147 uint32_t count = 1;
148 for (InternalIndex entry = FirstProbe(hash, capacity_);;
149 entry = NextProbe(entry, count++, capacity_)) {
150 // TODO(leszeks): Consider delaying the decompression until after the
151 // comparisons against empty/deleted.
152 Tagged<Object> element = derived_this->GetKey(cage_base, entry);
153 if (!IsKey(element)) return entry;
154 }
155}
156
157template <typename Derived>
158template <typename IsolateT, typename FindKey>
160 IsolateT* isolate, FindKey key, uint32_t hash) const {
161 // The derived class must guarantee the hash table is never full.
162 DCHECK(HasSufficientCapacityToAdd(1));
163 const Derived* derived_this = static_cast<const Derived*>(this);
165 uint32_t count = 1;
166 for (InternalIndex entry = FirstProbe(hash, capacity_);;
167 entry = NextProbe(entry, count++, capacity_)) {
168 // TODO(leszeks): Consider delaying the decompression until after the
169 // comparisons against empty/deleted.
170 Tagged<Object> element = derived_this->GetKey(isolate, entry);
171 if (element == empty_element()) {
172 // Empty entry, it's our insertion entry if there was no previous Hole.
173 if (insertion_entry.is_not_found()) return entry;
174 return insertion_entry;
175 }
176
177 if (element == deleted_element()) {
178 // Holes are potential insertion candidates, but we continue the search
179 // in case we find the actual matching entry.
180 if (insertion_entry.is_not_found()) insertion_entry = entry;
181 continue;
182 }
183
184 if (Derived::KeyIsMatch(isolate, key, element)) return entry;
185 }
186}
187
188// static
189template <typename Derived>
190template <typename Container, size_t OffsetOfElementsInContainer>
192 // Make sure that the elements_ array is at the end of Container, with no
193 // padding, so that subsequent elements can be accessed as offsets from
194 // elements_.
195 static_assert(OffsetOfElementsInContainer ==
196 sizeof(Container) - sizeof(Tagged_t));
197 // Make sure that elements_ is aligned when Container is aligned.
198 static_assert(OffsetOfElementsInContainer % kTaggedSize == 0);
199
201 sizeof(Container) + GetSizeExcludingHeader(capacity),
202 std::max(alignof(Container), alignof(void*)));
203}
204
205// static
206template <typename Derived>
208 AlignedFree(table);
209}
210
211} // namespace internal
212} // namespace v8
213
214#endif // V8_OBJECTS_OFF_HEAP_HASH_TABLE_INL_H_
static InternalIndex NotFound()
InternalIndex FindEntryOrInsertionEntry(IsolateT *isolate, FindKey key, uint32_t hash) const
InternalIndex FindEntry(IsolateT *isolate, FindKey key, uint32_t hash) const
void RehashInto(PtrComprCageBase cage_base, Derived *new_table)
bool HasSufficientCapacityToAdd(int number_of_additional_elements) const
bool ShouldResizeToAdd(int number_of_additional_elements, int *new_capacity)
void IterateElements(Root root, RootVisitor *visitor)
static int ComputeCapacity(int at_least_space_for)
static int ComputeCapacityWithShrink(int current_capacity, int at_least_space_for)
OffHeapObjectSlot slot(InternalIndex index, int offset=0) const
static constexpr Tagged< Smi > empty_element()
InternalIndex FindInsertionEntry(PtrComprCageBase cage_base, uint32_t hash) const
virtual void VisitRootPointers(Root root, const char *description, FullObjectSlot start, FullObjectSlot end)=0
V8_BASE_EXPORT constexpr uint32_t RoundUpToPowerOfTwo32(uint32_t value)
Definition bits.h:219
constexpr int kTaggedSize
Definition globals.h:542
void * AlignedAllocWithRetry(size_t size, size_t alignment)
void MemsetTagged(Tagged_t *start, Tagged< MaybeObject > value, size_t counter)
Definition slots-inl.h:486
Address Tagged_t
Definition globals.h:547
void AlignedFree(void *ptr)
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489