v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
layered-hash-map.h
Go to the documentation of this file.
1// Copyright 2022 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_COMPILER_TURBOSHAFT_LAYERED_HASH_MAP_H_
6#define V8_COMPILER_TURBOSHAFT_LAYERED_HASH_MAP_H_
7
8#include <cstddef>
9#include <iostream>
10#include <limits>
11#include <optional>
12
13#include "src/base/bits.h"
16
18
19// LayeredHashMap is a hash map whose elements are groupped into layers, such
20// that it's efficient to remove all of the items from the last inserted layer.
21// In addition to the regular Insert/Get/Contains functions of hash maps, it
22// thus provides two additional functions: StartLayer to indicate that future
23// insertions are part of a new layer, and DropLastLayer to remove all of the
24// items of the last layer.
25//
26// LayeredHashMap does not support inserting multiple values with the same key,
27// and does not support updating already-inserted items in the map. If you need
28// to update an existing key, you'll need to remove it (by calling DropLastLayer
29// as many times as needed), and then re-insert it.
30//
31// The implementation uses a regular ZoneVector for the main hash table, while
32// keeping a linked list of items per layer. When inserting an item in the
33// LayeredHashMap, we insert it into the ZoneVector and link it to the linked
34// list of the current (=latest) layer. In order to remove all of the items from
35// the last layer, we iterate its linked list, and remove the items one by one
36// from the ZoneVector, after which we drop the linked list alltogether.
37
38template <class Key, class Value>
40 public:
41 explicit LayeredHashMap(Zone* zone, uint32_t initial_capacity = 64);
42
43 void StartLayer();
45
46 void InsertNewKey(Key key, Value value);
47 bool Contains(Key key);
48 std::optional<Value> Get(Key key);
49
50 private:
51 struct Entry {
52 size_t hash = 0;
53 Key key = Key();
54 Value value = Value();
56 };
58 size_t NextEntryIndex(size_t index) { return (index + 1) & mask_; }
59 Entry* FindEntryForKey(Key key, size_t hash = 0);
61
62 size_t ComputeHash(Key key) {
63 size_t hash = fast_hash<Key>()(key);
64 return V8_UNLIKELY(hash == 0) ? 1 : hash;
65 }
66
67 size_t mask_;
72
73 static constexpr double kNeedResizePercentage = 0.75;
74 static constexpr int kGrowthFactor = 2;
75};
76
77template <class Key, class Value>
79 uint32_t initial_capacity)
80 : entry_count_(0), depths_heads_(zone), zone_(zone) {
81 // Setting the minimal capacity to 16
82 initial_capacity = std::max<uint32_t>(initial_capacity, 16);
83 // {initial_capacity} should be a power of 2, so that we can compute offset
84 // in {table_} with a mask rather than a modulo.
85 initial_capacity = base::bits::RoundUpToPowerOfTwo32(initial_capacity);
86 mask_ = initial_capacity - 1;
87 // Allocating the table_
88 table_ = zone_->NewVector<Entry>(initial_capacity);
89}
90
91template <class Key, class Value>
93 depths_heads_.push_back(nullptr);
94}
95
96template <class Key, class Value>
98 DCHECK_GT(depths_heads_.size(), 0);
99 for (Entry* entry = depths_heads_.back(); entry != nullptr;) {
100 entry_count_--;
101 Entry* next = entry->depth_neighboring_entry;
102 *entry = Entry();
103 entry = next;
104 }
105 depths_heads_.pop_back();
106}
107
108template <class Key, class Value>
111 for (size_t i = hash & mask_;; i = NextEntryIndex(i)) {
112 if (table_[i].hash == 0) return &table_[i];
113 if (table_[i].hash == hash && table_[i].key == key) return &table_[i];
114 }
115}
116
117template <class Key, class Value>
119 ResizeIfNeeded();
120 size_t hash = ComputeHash(key);
121 Entry* destination = FindEntryForKey(key, hash);
122 DCHECK_EQ(destination->hash, 0);
123 *destination = Entry{hash, key, value, depths_heads_.back()};
124 depths_heads_.back() = destination;
125 entry_count_++;
126}
127
128template <class Key, class Value>
129std::optional<Value> LayeredHashMap<Key, Value>::Get(Key key) {
130 Entry* destination = FindEntryForKey(key, ComputeHash(key));
131 if (destination->hash == 0) return std::nullopt;
132 return destination->value;
133}
134
135template <class Key, class Value>
137 return Get(key).has_value();
138}
139
140template <class Key, class Value>
142 if (table_.size() * kNeedResizePercentage > entry_count_) return;
143 CHECK_LE(table_.size(), std::numeric_limits<size_t>::max() / kGrowthFactor);
144 table_ = zone_->NewVector<Entry>(table_.size() * kGrowthFactor);
145 mask_ = table_.size() - 1;
147 sizeof(mask_) * 8 - base::bits::CountLeadingZeros(mask_));
148 for (size_t depth_idx = 0; depth_idx < depths_heads_.size(); depth_idx++) {
149 // It's important to fill the new hash by inserting data in increasing
150 // depth order, in order to avoid holes when later calling DropLastLayer.
151 // Consider for instance:
152 //
153 // ---+------+------+------+----
154 // | a1 | a2 | a3 |
155 // ---+------+------+------+----
156 //
157 // Where a1, a2 and a3 have the same hash. By construction, we know that
158 // depth(a1) <= depth(a2) <= depth(a3). If, when re-hashing, we were to
159 // insert them in another order, say:
160 //
161 // ---+------+------+------+----
162 // | a3 | a1 | a2 |
163 // ---+------+------+------+----
164 //
165 // Then, when we'll call DropLastLayer to remove entries from a3's depth,
166 // we'll get this:
167 //
168 // ---+------+------+------+----
169 // | null | a1 | a2 |
170 // ---+------+------+------+----
171 //
172 // And, when looking if a1 is in the hash, we'd find a "null" where we
173 // expect it, and assume that it's not present. If, instead, we always
174 // conserve the increasing depth order, then when removing a3, we'd get:
175 //
176 // ---+------+------+------+----
177 // | a1 | a2 | null |
178 // ---+------+------+------+----
179 //
180 // Where we can still find a1 and a2.
181 Entry* entry = depths_heads_[depth_idx];
182 depths_heads_[depth_idx] = nullptr;
183 while (entry != nullptr) {
184 Entry* new_entry_loc = FindEntryForKey(entry->key, entry->hash);
185 *new_entry_loc = *entry;
186 Entry* next_entry = entry->depth_neighboring_entry;
187 new_entry_loc->depth_neighboring_entry = depths_heads_[depth_idx];
188 depths_heads_[depth_idx] = new_entry_loc;
189 entry = next_entry;
190 }
191 }
192}
193
194} // namespace v8::internal::compiler::turboshaft
195
196#endif // V8_COMPILER_TURBOSHAFT_LAYERED_HASH_MAP_H_
base::Vector< T > NewVector(size_t length)
Definition zone.h:143
LayeredHashMap(Zone *zone, uint32_t initial_capacity=64)
Zone * zone_
InstructionOperand destination
constexpr unsigned CountLeadingZeros(T value)
Definition bits.h:100
constexpr unsigned CountPopulation(T value)
Definition bits.h:26
V8_BASE_EXPORT constexpr uint32_t RoundUpToPowerOfTwo32(uint32_t value)
Definition bits.h:219
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
return value
Definition map-inl.h:893
SourcePositionTable *const table_
Definition pipeline.cc:227
#define CHECK_LE(lhs, rhs)
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define V8_UNLIKELY(condition)
Definition v8config.h:660