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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | src/interpreter/constant-array-builder.h » ('j') | src/interpreter/constant-array-builder.h » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/inline-hash-map.h
diff --git a/src/inline-hash-map.h b/src/inline-hash-map.h
new file mode 100644
index 0000000000000000000000000000000000000000..30256b05626d26f3fcebb4b8cc23ef6a337fe605
--- /dev/null
+++ b/src/inline-hash-map.h
@@ -0,0 +1,500 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_ZONE_TEMPLATE_HASH_MAP_H_
+#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
+
+#include "src/zone.h"
+
+namespace v8 {
+namespace internal {
rmcilroy 2016/09/12 14:17:31 This should really be v8/base (and in the base dir
+
+class DefaultAllocationPolicy {
+ public:
+ V8_INLINE void* New(size_t size) { return malloc(size); }
+ V8_INLINE static void Delete(void* p) { free(p); }
+};
+
+namespace detail {
rmcilroy 2016/09/12 14:17:31 Typically we use annoymous namespaces for private
+
+template <typename Key, typename Value>
+class InlineHashMapEntry {
+ public:
+ InlineHashMapEntry(Key key, Value value, size_t hash)
+ : keyValue_(std::move(key), std::move(value)),
+ hash_(hash),
+ exists_(true) {}
+
+ const std::pair<const Key, Value>& keyValue() const { return keyValue_; }
+ std::pair<const Key, Value>& keyValue() { return keyValue_; }
+
+ size_t hash() const { return hash_; }
+
+ bool exists() const { return exists_; }
+
+ void clear() { exists_ = false; }
+
+ bool operator==(const InlineHashMapEntry& other) {
+ if (!exists()) {
+ return !other.exists();
+ }
+
+ if (!other.exists()) {
+ return false;
+ }
+
+ return keyValue() == other.keyValue();
+ }
+
+ bool operator!=(const InlineHashMapEntry& other) { return !(*this == other); }
+
+ private:
+ std::pair<const Key, Value> keyValue_;
+ size_t hash_; // The full hash value for key
+ bool exists_; // TODO(leszeks): this could be a tagged hash
+};
+
+// Specialize InlineHashMapEntry for pointer types
+template <typename Key, typename Value>
+class InlineHashMapEntry<Key*, Value> {
+ public:
+ InlineHashMapEntry(Key* key, Value value, size_t hash)
+ : keyValue_(std::move(key), std::move(value)), hash_(hash) {}
+
+ const std::pair<Key* const, Value>& keyValue() const { return keyValue_; }
+ std::pair<Key* const, Value>& keyValue() { return keyValue_; }
+
+ size_t hash() const { return hash_; }
+
+ bool exists() const { return keyValue_.first != nullptr; }
+
+ void clear() {
+ // Nasty const_cast to allow us to set the normally const key of the entry.
+ // Only valid because we absolutely promise that we're not using this object
+ // except for existence tests.
+ const_cast<Key*&>(keyValue_.first) = nullptr;
+ }
+
+ bool operator==(const InlineHashMapEntry& other) {
+ if (!exists()) {
+ return !other.exists();
+ }
+
+ if (!other.exists()) {
+ return false;
+ }
+
+ return keyValue() == other.keyValue();
+ }
+
+ bool operator!=(const InlineHashMapEntry& other) { return !(*this == other); }
+
+ private:
+ std::pair<Key* const, Value> keyValue_;
+ size_t hash_; // The full hash value for key
+};
+} // namespace detail
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy = DefaultAllocationPolicy>
+class InlineHashMap
+ : private AllocationPolicy /* for Empty Base Optimization */ {
+ public:
+ // The default capacity. This is used by the call sites which want
+ // to pass in a non-default AllocationPolicy but want to use the
+ // default value of capacity specified by the implementation.
+ static const uint32_t kDefaultHashMapCapacity = 8;
+
+ // initial_capacity is the size of the initial hash map;
+ // it must be a power of 2 (and thus must not be 0).
+ explicit InlineHashMap(uint32_t capacity = kDefaultHashMapCapacity,
+ AllocationPolicy allocator = AllocationPolicy());
+
+ ~InlineHashMap();
+
+ class Iterator;
+ typedef Iterator iterator;
+ typedef const Iterator const_iterator;
+
+ // If an entry with matching key is found, returns an iterator to that entry.
+ // Otherwise, the end() iterator is returned
+ iterator Find(const Key& key) const;
+
+ // Inserts the given key and value into the map, returning true if there was
+ // already another entry under that key
+ bool Insert(Key key, Value value);
+
+ // If an entry with matching key is found, returns the value of that entry.
+ // Otherwise, it creates the value using the given function, inserts it, and
+ // returns it
+ template <typename Func>
+ Value& LookupOrInsert(const Key& key, const Func& valueFunc);
+
+ // If an entry with matching key is found, returns the value of that entry.
+ // Otherwise, it creates the value with its default constructor, inserts it,
+ // and returns it
+ Value& LookupOrInsert(const Key& key);
+
+ Value& operator[](const Key& key);
+
+ iterator start();
+
+ iterator end();
+
+ // Removes the entry with matching key.
+ // Returns true if there was an entry.
+ bool Remove(const Key& key);
+
+ void Clear();
+
+ // The number of (non-empty) entries in the table.
+ uint32_t occupancy() const { return occupancy_; }
+
+ // The capacity of the table. The implementation
+ // makes sure that occupancy is at most 80% of
+ // the table capacity.
+ uint32_t capacity() const { return capacity_; }
+
+ private:
+ typedef detail::InlineHashMapEntry<Key, Value> Entry;
+
+ Entry* map_;
+ uint32_t capacity_;
+ uint32_t occupancy_;
+
+ Entry* map_end() const { return map_ + capacity_; }
+
+ void EnsureInitialized() const;
+ void Initialize(uint32_t capacity);
+
+ Entry* Probe(const Key& key, size_t hash) const;
+ Entry* FillProbe(Entry* entry, Key key, Value value, size_t hashval);
+ Entry* ResizeAndProbe(const Key& key, size_t hash);
+};
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::InlineHashMap(
+ uint32_t initial_capacity, AllocationPolicy allocator)
+ : AllocationPolicy(allocator),
+ map_(nullptr),
rmcilroy 2016/09/12 14:17:31 As discussed offline, let's make this just eagerly
+ capacity_(initial_capacity),
+ occupancy_(0) {
+ // initialize lazily
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::~InlineHashMap() {
+ AllocationPolicy::Delete(map_);
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::iterator
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::Find(const Key& key) const {
+ Entry* p = Probe(key, Hash()(key));
+ return Iterator(p->exists() ? p : map_end(), map_end());
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+bool InlineHashMap<Key, Value, Hash, AllocationPolicy>::Insert(Key key,
+ Value value) {
+ size_t hashval = Hash()(key);
+ Entry* p = Probe(key, hashval);
+ bool valueReplaced = p->exists();
+ p = FillProbe(p, key, Value(), hashval);
+ return valueReplaced;
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+template <typename Func>
+Value& InlineHashMap<Key, Value, Hash, AllocationPolicy>::LookupOrInsert(
+ const Key& key, const Func& valueFunc) {
+ size_t hashval = Hash()(key);
+ Entry* p = Probe(key, hashval);
+ if (!p->exists()) {
+ p = FillProbe(p, key, valueFunc(), hashval);
+ }
+ return p->keyValue().second;
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+Value& InlineHashMap<Key, Value, Hash, AllocationPolicy>::LookupOrInsert(
+ const Key& key) {
+ return LookupOrInsert(key, []() { return Value(); });
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+Value& InlineHashMap<Key, Value, Hash, AllocationPolicy>::operator[](
+ const Key& key) {
+ return LookupOrInsert(key);
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::iterator
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::start() {
+ if (map_ == nullptr) {
+ return Iterator(nullptr, nullptr);
+ }
+ // Increment from map_ - 1 to find the first entry that exists
+ return ++Iterator(map_ - 1, map_end());
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::iterator
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::end() {
+ if (map_ == nullptr) {
+ return Iterator(nullptr, nullptr);
+ }
+ return Iterator(map_end(), map_end());
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+void InlineHashMap<Key, Value, Hash, AllocationPolicy>::EnsureInitialized()
+ const {
+ if (map_ == nullptr) {
+ // Cast away the constness of "this", because this is a lazy
+ // initialization and "virtually" happens in the constructor
+ const_cast<InlineHashMap*>(this)->Initialize(capacity_);
+ }
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+void InlineHashMap<Key, Value, Hash, AllocationPolicy>::Initialize(
+ uint32_t capacity) {
+ DCHECK(base::bits::IsPowerOfTwo32(capacity));
+ map_ =
+ reinterpret_cast<Entry*>(AllocationPolicy::New(capacity * sizeof(Entry)));
+ if (map_ == nullptr) {
+ FATAL("Out of memory: HashMap::Initialize");
+ return;
+ }
+ capacity_ = capacity;
+ Clear();
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+bool InlineHashMap<Key, Value, Hash, AllocationPolicy>::Remove(const Key& key) {
+ if (map_ == nullptr) {
+ return false;
+ }
+
+ size_t hashval = Hash()(key);
+ // Lookup the entry for the key to remove.
+ Entry* p = Probe(key, hashval);
+ if (!p->exists()) {
+ // Key not found nothing to remove.
+ return false;
+ }
+
+ // To remove an entry we need to ensure that it does not create an empty
+ // entry that will cause the search for another entry to stop too soon. If all
+ // the entries between the entry to remove and the next empty slot have their
+ // initial position inside this interval, clearing the entry to remove will
+ // not break the search. If, while searching for the next empty entry, an
+ // entry is encountered which does not have its initial position between the
+ // entry to remove and the position looked at, then this entry can be moved to
+ // the place of the entry to remove without breaking the search for it. The
+ // entry made vacant by this move is now the entry to remove and the process
+ // starts over.
+ // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
+
+ // This guarantees loop termination as there is at least one empty entry so
+ // eventually the removed entry will have an empty entry after it.
+ DCHECK(occupancy_ < capacity_);
+
+ // p is the candidate entry to clear. q is used to scan forwards.
+ Entry* q = p; // Start at the entry to remove.
+ while (true) {
+ // Move q to the next entry.
+ q = q + 1;
+ if (q == map_end()) {
+ q = map_;
+ }
+
+ // All entries between p and q have their initial position between p and q
+ // and the entry p can be cleared without breaking the search for these
+ // entries.
+ if (!q->exists()) {
+ break;
+ }
+
+ // Find the initial position for the entry at position q.
+ Entry* r = map_ + (q->hash & (capacity_ - 1));
+
+ // If the entry at position q has its initial position outside the range
+ // between p and q it can be moved forward to position p and will still be
+ // found. There is now a new candidate entry for clearing.
+ if ((q > p && (r <= p || r > q)) || (q < p && (r <= p && r > q))) {
+ *p = *q;
+ p = q;
+ }
+ }
+
+ // Clear the entry which is allowed to be emptied.
+ p->clear();
+ occupancy_--;
+ return true;
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+void InlineHashMap<Key, Value, Hash, AllocationPolicy>::Clear() {
+ for (Entry* e = map_; e < map_end(); ++e) {
+ e->clear();
+ }
+ occupancy_ = 0;
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::Entry*
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::Probe(const Key& key,
+ size_t hash) const {
+ DCHECK(base::bits::IsPowerOfTwo32(capacity_));
+ EnsureInitialized();
+
+ Entry* p = map_ + (hash & (capacity_ - 1));
+ const Entry* end = map_end();
+ DCHECK(map_ <= p && p < end);
+
+ DCHECK(occupancy_ < capacity_); // Guarantees loop termination.
+ while (p->exists() && (hash != p->hash() || key != p->keyValue().first)) {
+ p++;
+ if (p >= end) {
+ p = map_;
+ }
+ }
+
+ return p;
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::Entry*
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::FillProbe(Entry* entry,
+ Key key,
+ Value value,
+ size_t hashval) {
+ DCHECK(!entry->exists());
+
+ // No entry found; insert one.
+ new (entry) Entry(std::move(key), std::move(value), hashval);
+ occupancy_++;
+
+ // Grow the map if we reached >= 80% occupancy.
+ if (occupancy_ + occupancy_ / 4 >= capacity_) {
+ entry = ResizeAndProbe(key, hashval);
+ }
+
+ return entry;
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+typename InlineHashMap<Key, Value, Hash, AllocationPolicy>::Entry*
+InlineHashMap<Key, Value, Hash, AllocationPolicy>::ResizeAndProbe(
+ const Key& recoveredKey, size_t recoveredHash) {
+ Entry* map = map_;
+ Entry* recoveredVal = nullptr;
+ uint32_t n = occupancy_;
+
+ // Allocate larger map.
+ Initialize(capacity_ * 2);
+
+ // Rehash all current entries.
+ for (Entry* p = map; n > 0; p++) {
+ if (p->exists()) {
+ Entry* new_p = Probe(p->keyValue().first, p->hash());
+ // Manually fill the probed entry to skip the occupancy check
+ DCHECK(!new_p->exists());
+ new (new_p) Entry(std::move(p->keyValue().first),
+ std::move(p->keyValue().second), p->hash());
+ occupancy_++;
+ n--;
+
+ // If this entry is the one that caused the resize, save its new pointer
+ // This saves us doing another probe after the resize.
+ if (recoveredVal == nullptr && p->hash() == recoveredHash &&
+ p->keyValue().first == recoveredKey) {
+ recoveredVal = new_p;
+ }
+ }
+ }
+
+ // Delete previous map
+ AllocationPolicy::Delete(map);
+
+ DCHECK(recoveredVal != nullptr);
+ return recoveredVal;
+}
+
+template <typename Key, typename Value, typename Hash,
+ typename AllocationPolicy>
+class InlineHashMap<Key, Value, Hash, AllocationPolicy>::Iterator {
+ public:
+ Iterator() : entry_(nullptr), end_(nullptr) {}
+ Iterator(Entry* start, Entry* end) : entry_(start), end_(end) {}
+
+ std::pair<const Key, Value>& operator*() { return entry_->keyValue(); }
+ const std::pair<const Key, Value>& operator*() const {
+ return entry_->keyValue();
+ }
+
+ std::pair<const Key, Value>* operator->() { return &entry_->keyValue(); }
+ const std::pair<const Key, Value>* operator->() const {
+ return &entry_->keyValue();
+ }
+
+ Iterator operator++() {
+ ++entry_;
+ while (entry_ < end_ && !entry_->exists()) {
+ ++entry_;
+ }
+ return *this;
+ }
+
+ Iterator operator++(int) {
+ Iterator ret(*this);
+ this->operator++();
+ return ret;
+ }
+
+ bool operator==(const Iterator& other) const {
+ return entry_ == other.entry_;
+ }
+
+ bool operator!=(const Iterator& other) const { return entry_ != other.entry; }
+
+ private:
+ Entry* entry_;
+ Entry* end_;
+};
+
+template <typename Key, typename Value, typename Hash>
+class ZoneInlineHashMap
+ : public InlineHashMap<Key, Value, Hash, ZoneAllocationPolicy> {
+ public:
+ explicit ZoneInlineHashMap(
+ Zone* zone,
+ uint32_t capacity = InlineHashMap<
+ Key, Value, ZoneAllocationPolicy>::kDefaultHashMapCapacity)
+ : InlineHashMap<Key, Value, Hash, ZoneAllocationPolicy>(
+ capacity, ZoneAllocationPolicy(zone)) {}
+};
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_ZONE_TEMPLATE_HASH_MAP_H_
« 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