Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(647)

Side by Side Diff: src/inline-hash-map.h

Issue 2336553002: [interpreter] Use hashmap for ConstantArrayBuilder's constant map (Closed)
Patch Set: Rebased onto master Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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_ZONE_TEMPLATE_HASH_MAP_H_
6 #define V8_ZONE_TEMPLATE_HASH_MAP_H_
rmcilroy 2016/09/12 14:17:31 I wonder how much of this should really live in th
7
8 #include "src/zone.h"
9
10 namespace v8 {
11 namespace internal {
rmcilroy 2016/09/12 14:17:31 This should really be v8/base (and in the base dir
12
13 class DefaultAllocationPolicy {
14 public:
15 V8_INLINE void* New(size_t size) { return malloc(size); }
16 V8_INLINE static void Delete(void* p) { free(p); }
17 };
18
19 namespace detail {
rmcilroy 2016/09/12 14:17:31 Typically we use annoymous namespaces for private
20
21 template <typename Key, typename Value>
22 class InlineHashMapEntry {
23 public:
24 InlineHashMapEntry(Key key, Value value, size_t hash)
25 : keyValue_(std::move(key), std::move(value)),
26 hash_(hash),
27 exists_(true) {}
28
29 const std::pair<const Key, Value>& keyValue() const { return keyValue_; }
30 std::pair<const Key, Value>& keyValue() { return keyValue_; }
31
32 size_t hash() const { return hash_; }
33
34 bool exists() const { return exists_; }
35
36 void clear() { exists_ = false; }
37
38 bool operator==(const InlineHashMapEntry& other) {
39 if (!exists()) {
40 return !other.exists();
41 }
42
43 if (!other.exists()) {
44 return false;
45 }
46
47 return keyValue() == other.keyValue();
48 }
49
50 bool operator!=(const InlineHashMapEntry& other) { return !(*this == other); }
51
52 private:
53 std::pair<const Key, Value> keyValue_;
54 size_t hash_; // The full hash value for key
55 bool exists_; // TODO(leszeks): this could be a tagged hash
56 };
57
58 // Specialize InlineHashMapEntry for pointer types
59 template <typename Key, typename Value>
60 class InlineHashMapEntry<Key*, Value> {
61 public:
62 InlineHashMapEntry(Key* key, Value value, size_t hash)
63 : keyValue_(std::move(key), std::move(value)), hash_(hash) {}
64
65 const std::pair<Key* const, Value>& keyValue() const { return keyValue_; }
66 std::pair<Key* const, Value>& keyValue() { return keyValue_; }
67
68 size_t hash() const { return hash_; }
69
70 bool exists() const { return keyValue_.first != nullptr; }
71
72 void clear() {
73 // Nasty const_cast to allow us to set the normally const key of the entry.
74 // Only valid because we absolutely promise that we're not using this object
75 // except for existence tests.
76 const_cast<Key*&>(keyValue_.first) = nullptr;
77 }
78
79 bool operator==(const InlineHashMapEntry& other) {
80 if (!exists()) {
81 return !other.exists();
82 }
83
84 if (!other.exists()) {
85 return false;
86 }
87
88 return keyValue() == other.keyValue();
89 }
90
91 bool operator!=(const InlineHashMapEntry& other) { return !(*this == other); }
92
93 private:
94 std::pair<Key* const, Value> keyValue_;
95 size_t hash_; // The full hash value for key
96 };
97 } // namespace detail
98
99 template <typename Key, typename Value, typename Hash,
100 typename AllocationPolicy = DefaultAllocationPolicy>
101 class InlineHashMap
102 : private AllocationPolicy /* for Empty Base Optimization */ {
103 public:
104 // The default capacity. This is used by the call sites which want
105 // to pass in a non-default AllocationPolicy but want to use the
106 // default value of capacity specified by the implementation.
107 static const uint32_t kDefaultHashMapCapacity = 8;
108
109 // initial_capacity is the size of the initial hash map;
110 // it must be a power of 2 (and thus must not be 0).
111 explicit InlineHashMap(uint32_t capacity = kDefaultHashMapCapacity,
112 AllocationPolicy allocator = AllocationPolicy());
113
114 ~InlineHashMap();
115
116 class Iterator;
117 typedef Iterator iterator;
118 typedef const Iterator const_iterator;
119
120 // If an entry with matching key is found, returns an iterator to that entry.
121 // Otherwise, the end() iterator is returned
122 iterator Find(const Key& key) const;
123
124 // Inserts the given key and value into the map, returning true if there was
125 // already another entry under that key
126 bool Insert(Key key, Value value);
127
128 // If an entry with matching key is found, returns the value of that entry.
129 // Otherwise, it creates the value using the given function, inserts it, and
130 // returns it
131 template <typename Func>
132 Value& LookupOrInsert(const Key& key, const Func& valueFunc);
133
134 // If an entry with matching key is found, returns the value of that entry.
135 // Otherwise, it creates the value with its default constructor, inserts it,
136 // and returns it
137 Value& LookupOrInsert(const Key& key);
138
139 Value& operator[](const Key& key);
140
141 iterator start();
142
143 iterator end();
144
145 // Removes the entry with matching key.
146 // Returns true if there was an entry.
147 bool Remove(const Key& key);
148
149 void Clear();
150
151 // The number of (non-empty) entries in the table.
152 uint32_t occupancy() const { return occupancy_; }
153
154 // The capacity of the table. The implementation
155 // makes sure that occupancy is at most 80% of
156 // the table capacity.
157 uint32_t capacity() const { return capacity_; }
158
159 private:
160 typedef detail::InlineHashMapEntry<Key, Value> Entry;
161
162 Entry* map_;
163 uint32_t capacity_;
164 uint32_t occupancy_;
165
166 Entry* map_end() const { return map_ + capacity_; }
167
168 void EnsureInitialized() const;
169 void Initialize(uint32_t capacity);
170
171 Entry* Probe(const Key& key, size_t hash) const;
172 Entry* FillProbe(Entry* entry, Key key, Value value, size_t hashval);
173 Entry* ResizeAndProbe(const Key& key, size_t hash);
174 };
175
176 template <typename Key, typename Value, typename Hash,
177 typename AllocationPolicy>
178 InlineHashMap<Key, Value, Hash, AllocationPolicy>::InlineHashMap(
179 uint32_t initial_capacity, AllocationPolicy allocator)
180 : AllocationPolicy(allocator),
181 map_(nullptr),
rmcilroy 2016/09/12 14:17:31 As discussed offline, let's make this just eagerly
182 capacity_(initial_capacity),
183 occupancy_(0) {
184 // initialize lazily
185 }
186
187 template <typename Key, typename Value, typename Hash,
188 typename AllocationPolicy>
189 InlineHashMap<Key, Value, Hash, AllocationPolicy>::~InlineHashMap() {
190 AllocationPolicy::Delete(map_);
191 }
192
193 template <typename Key, typename Value, typename Hash,
194 typename AllocationPolicy>
195 typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::iterator
196 InlineHashMap<Key, Value, Hash, AllocationPolicy>::Find(const Key& key) const {
197 Entry* p = Probe(key, Hash()(key));
198 return Iterator(p->exists() ? p : map_end(), map_end());
199 }
200
201 template <typename Key, typename Value, typename Hash,
202 typename AllocationPolicy>
203 bool InlineHashMap<Key, Value, Hash, AllocationPolicy>::Insert(Key key,
204 Value value) {
205 size_t hashval = Hash()(key);
206 Entry* p = Probe(key, hashval);
207 bool valueReplaced = p->exists();
208 p = FillProbe(p, key, Value(), hashval);
209 return valueReplaced;
210 }
211
212 template <typename Key, typename Value, typename Hash,
213 typename AllocationPolicy>
214 template <typename Func>
215 Value& InlineHashMap<Key, Value, Hash, AllocationPolicy>::LookupOrInsert(
216 const Key& key, const Func& valueFunc) {
217 size_t hashval = Hash()(key);
218 Entry* p = Probe(key, hashval);
219 if (!p->exists()) {
220 p = FillProbe(p, key, valueFunc(), hashval);
221 }
222 return p->keyValue().second;
223 }
224
225 template <typename Key, typename Value, typename Hash,
226 typename AllocationPolicy>
227 Value& InlineHashMap<Key, Value, Hash, AllocationPolicy>::LookupOrInsert(
228 const Key& key) {
229 return LookupOrInsert(key, []() { return Value(); });
230 }
231
232 template <typename Key, typename Value, typename Hash,
233 typename AllocationPolicy>
234 Value& InlineHashMap<Key, Value, Hash, AllocationPolicy>::operator[](
235 const Key& key) {
236 return LookupOrInsert(key);
237 }
238
239 template <typename Key, typename Value, typename Hash,
240 typename AllocationPolicy>
241 typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::iterator
242 InlineHashMap<Key, Value, Hash, AllocationPolicy>::start() {
243 if (map_ == nullptr) {
244 return Iterator(nullptr, nullptr);
245 }
246 // Increment from map_ - 1 to find the first entry that exists
247 return ++Iterator(map_ - 1, map_end());
248 }
249
250 template <typename Key, typename Value, typename Hash,
251 typename AllocationPolicy>
252 typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::iterator
253 InlineHashMap<Key, Value, Hash, AllocationPolicy>::end() {
254 if (map_ == nullptr) {
255 return Iterator(nullptr, nullptr);
256 }
257 return Iterator(map_end(), map_end());
258 }
259
260 template <typename Key, typename Value, typename Hash,
261 typename AllocationPolicy>
262 void InlineHashMap<Key, Value, Hash, AllocationPolicy>::EnsureInitialized()
263 const {
264 if (map_ == nullptr) {
265 // Cast away the constness of "this", because this is a lazy
266 // initialization and "virtually" happens in the constructor
267 const_cast<InlineHashMap*>(this)->Initialize(capacity_);
268 }
269 }
270
271 template <typename Key, typename Value, typename Hash,
272 typename AllocationPolicy>
273 void InlineHashMap<Key, Value, Hash, AllocationPolicy>::Initialize(
274 uint32_t capacity) {
275 DCHECK(base::bits::IsPowerOfTwo32(capacity));
276 map_ =
277 reinterpret_cast<Entry*>(AllocationPolicy::New(capacity * sizeof(Entry)));
278 if (map_ == nullptr) {
279 FATAL("Out of memory: HashMap::Initialize");
280 return;
281 }
282 capacity_ = capacity;
283 Clear();
284 }
285
286 template <typename Key, typename Value, typename Hash,
287 typename AllocationPolicy>
288 bool InlineHashMap<Key, Value, Hash, AllocationPolicy>::Remove(const Key& key) {
289 if (map_ == nullptr) {
290 return false;
291 }
292
293 size_t hashval = Hash()(key);
294 // Lookup the entry for the key to remove.
295 Entry* p = Probe(key, hashval);
296 if (!p->exists()) {
297 // Key not found nothing to remove.
298 return false;
299 }
300
301 // To remove an entry we need to ensure that it does not create an empty
302 // entry that will cause the search for another entry to stop too soon. If all
303 // the entries between the entry to remove and the next empty slot have their
304 // initial position inside this interval, clearing the entry to remove will
305 // not break the search. If, while searching for the next empty entry, an
306 // entry is encountered which does not have its initial position between the
307 // entry to remove and the position looked at, then this entry can be moved to
308 // the place of the entry to remove without breaking the search for it. The
309 // entry made vacant by this move is now the entry to remove and the process
310 // starts over.
311 // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
312
313 // This guarantees loop termination as there is at least one empty entry so
314 // eventually the removed entry will have an empty entry after it.
315 DCHECK(occupancy_ < capacity_);
316
317 // p is the candidate entry to clear. q is used to scan forwards.
318 Entry* q = p; // Start at the entry to remove.
319 while (true) {
320 // Move q to the next entry.
321 q = q + 1;
322 if (q == map_end()) {
323 q = map_;
324 }
325
326 // All entries between p and q have their initial position between p and q
327 // and the entry p can be cleared without breaking the search for these
328 // entries.
329 if (!q->exists()) {
330 break;
331 }
332
333 // Find the initial position for the entry at position q.
334 Entry* r = map_ + (q->hash & (capacity_ - 1));
335
336 // If the entry at position q has its initial position outside the range
337 // between p and q it can be moved forward to position p and will still be
338 // found. There is now a new candidate entry for clearing.
339 if ((q > p && (r <= p || r > q)) || (q < p && (r <= p && r > q))) {
340 *p = *q;
341 p = q;
342 }
343 }
344
345 // Clear the entry which is allowed to be emptied.
346 p->clear();
347 occupancy_--;
348 return true;
349 }
350
351 template <typename Key, typename Value, typename Hash,
352 typename AllocationPolicy>
353 void InlineHashMap<Key, Value, Hash, AllocationPolicy>::Clear() {
354 for (Entry* e = map_; e < map_end(); ++e) {
355 e->clear();
356 }
357 occupancy_ = 0;
358 }
359
360 template <typename Key, typename Value, typename Hash,
361 typename AllocationPolicy>
362 typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::Entry*
363 InlineHashMap<Key, Value, Hash, AllocationPolicy>::Probe(const Key& key,
364 size_t hash) const {
365 DCHECK(base::bits::IsPowerOfTwo32(capacity_));
366 EnsureInitialized();
367
368 Entry* p = map_ + (hash & (capacity_ - 1));
369 const Entry* end = map_end();
370 DCHECK(map_ <= p && p < end);
371
372 DCHECK(occupancy_ < capacity_); // Guarantees loop termination.
373 while (p->exists() && (hash != p->hash() || key != p->keyValue().first)) {
374 p++;
375 if (p >= end) {
376 p = map_;
377 }
378 }
379
380 return p;
381 }
382
383 template <typename Key, typename Value, typename Hash,
384 typename AllocationPolicy>
385 typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::Entry*
386 InlineHashMap<Key, Value, Hash, AllocationPolicy>::FillProbe(Entry* entry,
387 Key key,
388 Value value,
389 size_t hashval) {
390 DCHECK(!entry->exists());
391
392 // No entry found; insert one.
393 new (entry) Entry(std::move(key), std::move(value), hashval);
394 occupancy_++;
395
396 // Grow the map if we reached >= 80% occupancy.
397 if (occupancy_ + occupancy_ / 4 >= capacity_) {
398 entry = ResizeAndProbe(key, hashval);
399 }
400
401 return entry;
402 }
403
404 template <typename Key, typename Value, typename Hash,
405 typename AllocationPolicy>
406 typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::Entry*
407 InlineHashMap<Key, Value, Hash, AllocationPolicy>::ResizeAndProbe(
408 const Key& recoveredKey, size_t recoveredHash) {
409 Entry* map = map_;
410 Entry* recoveredVal = nullptr;
411 uint32_t n = occupancy_;
412
413 // Allocate larger map.
414 Initialize(capacity_ * 2);
415
416 // Rehash all current entries.
417 for (Entry* p = map; n > 0; p++) {
418 if (p->exists()) {
419 Entry* new_p = Probe(p->keyValue().first, p->hash());
420 // Manually fill the probed entry to skip the occupancy check
421 DCHECK(!new_p->exists());
422 new (new_p) Entry(std::move(p->keyValue().first),
423 std::move(p->keyValue().second), p->hash());
424 occupancy_++;
425 n--;
426
427 // If this entry is the one that caused the resize, save its new pointer
428 // This saves us doing another probe after the resize.
429 if (recoveredVal == nullptr && p->hash() == recoveredHash &&
430 p->keyValue().first == recoveredKey) {
431 recoveredVal = new_p;
432 }
433 }
434 }
435
436 // Delete previous map
437 AllocationPolicy::Delete(map);
438
439 DCHECK(recoveredVal != nullptr);
440 return recoveredVal;
441 }
442
443 template <typename Key, typename Value, typename Hash,
444 typename AllocationPolicy>
445 class InlineHashMap<Key, Value, Hash, AllocationPolicy>::Iterator {
446 public:
447 Iterator() : entry_(nullptr), end_(nullptr) {}
448 Iterator(Entry* start, Entry* end) : entry_(start), end_(end) {}
449
450 std::pair<const Key, Value>& operator*() { return entry_->keyValue(); }
451 const std::pair<const Key, Value>& operator*() const {
452 return entry_->keyValue();
453 }
454
455 std::pair<const Key, Value>* operator->() { return &entry_->keyValue(); }
456 const std::pair<const Key, Value>* operator->() const {
457 return &entry_->keyValue();
458 }
459
460 Iterator operator++() {
461 ++entry_;
462 while (entry_ < end_ && !entry_->exists()) {
463 ++entry_;
464 }
465 return *this;
466 }
467
468 Iterator operator++(int) {
469 Iterator ret(*this);
470 this->operator++();
471 return ret;
472 }
473
474 bool operator==(const Iterator& other) const {
475 return entry_ == other.entry_;
476 }
477
478 bool operator!=(const Iterator& other) const { return entry_ != other.entry; }
479
480 private:
481 Entry* entry_;
482 Entry* end_;
483 };
484
485 template <typename Key, typename Value, typename Hash>
486 class ZoneInlineHashMap
487 : public InlineHashMap<Key, Value, Hash, ZoneAllocationPolicy> {
488 public:
489 explicit ZoneInlineHashMap(
490 Zone* zone,
491 uint32_t capacity = InlineHashMap<
492 Key, Value, ZoneAllocationPolicy>::kDefaultHashMapCapacity)
493 : InlineHashMap<Key, Value, Hash, ZoneAllocationPolicy>(
494 capacity, ZoneAllocationPolicy(zone)) {}
495 };
496
497 } // namespace internal
498 } // namespace v8
499
500 #endif // V8_ZONE_TEMPLATE_HASH_MAP_H_
OLDNEW
« no previous file with comments | « no previous file | src/interpreter/constant-array-builder.h » ('j') | src/interpreter/constant-array-builder.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698