[tint][utils] Replace std::unordered_map with Hashset in UniqueAllocator
Bug: tint:2129
Change-Id: Ib8b8ebacf2ce778540a5112ad12c4f879f7df612
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/171661
Reviewed-by: dan sinclair <dsinclair@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/utils/containers/hashset.h b/src/tint/utils/containers/hashset.h
index 86fcc5f..9bdc7ef 100644
--- a/src/tint/utils/containers/hashset.h
+++ b/src/tint/utils/containers/hashset.h
@@ -105,6 +105,16 @@
}
return true;
}
+
+ /// Looks up an entry in the set that is equal to @p key
+ /// @param key the key to search for.
+ /// @returns the entry that is equal to @p key
+ std::optional<KEY> Get(const KEY& key) const {
+ if (auto [found, index] = this->IndexOf(key); found) {
+ return this->slots_[index].entry;
+ }
+ return std::nullopt;
+ }
};
} // namespace tint
diff --git a/src/tint/utils/containers/unique_allocator.h b/src/tint/utils/containers/unique_allocator.h
index 124adc4..8d88623 100644
--- a/src/tint/utils/containers/unique_allocator.h
+++ b/src/tint/utils/containers/unique_allocator.h
@@ -29,15 +29,14 @@
#define SRC_TINT_UTILS_CONTAINERS_UNIQUE_ALLOCATOR_H_
#include <functional>
-#include <unordered_set>
#include <utility>
+#include "src/tint/utils/containers/hashmap_base.h"
#include "src/tint/utils/memory/block_allocator.h"
namespace tint {
-/// UniqueAllocator is used to allocate unique instances of the template type
-/// `T`.
+/// UniqueAllocator is used to allocate unique instances of the template type `T`.
template <typename T, typename HASH = std::hash<T>, typename EQUAL = std::equal_to<T>>
class UniqueAllocator {
public:
@@ -50,19 +49,15 @@
/// pointer is returned.
template <typename TYPE = T, typename... ARGS>
TYPE* Get(ARGS&&... args) {
- // Create a temporary T instance on the stack so that we can hash it, and
- // use it for equality lookup for the std::unordered_set. If the item is not
- // found in the set, then we create the persisted instance with the
- // allocator.
- TYPE key{args...};
- auto hash = Hasher{}(key);
- auto it = items.find(Entry{hash, &key});
- if (it != items.end()) {
- return static_cast<TYPE*>(it->ptr);
+ // Create a temporary T instance on the stack so that we can hash it, and use it for
+ // equality lookup for the Set. If the item is not found in the set, then we create the
+ // persisted instance with the allocator.
+ TYPE prototype{args...};
+ Key& key = items.Add(&prototype);
+ if (key.Value() == &prototype) {
+ key = allocator.template Create<TYPE>(std::forward<ARGS>(args)...);
}
- auto* ptr = allocator.template Create<TYPE>(std::forward<ARGS>(args)...);
- items.emplace_hint(it, Entry{hash, ptr});
- return ptr;
+ return static_cast<TYPE*>(key.Value());
}
/// @param args the arguments used to create the temporary used for the search.
@@ -70,15 +65,10 @@
/// was not found.
template <typename TYPE = T, typename... ARGS>
TYPE* Find(ARGS&&... args) const {
- // Create a temporary T instance on the stack so that we can hash it, and
- // use it for equality lookup for the std::unordered_set.
- TYPE key{args...};
- auto hash = Hasher{}(key);
- auto it = items.find(Entry{hash, &key});
- if (it != items.end()) {
- return static_cast<TYPE*>(it->ptr);
- }
- return nullptr;
+ // Create a temporary T instance on the stack so that we can hash it, and use it for
+ // equality lookup for the Set.
+ TYPE prototype{std::forward<ARGS>(args)...};
+ return static_cast<TYPE*>(items.Get(&prototype));
}
/// Wrap sets this allocator to the objects created with the content of `inner`.
@@ -95,36 +85,52 @@
Iterator end() const { return allocator.Objects().end(); }
private:
- /// The hash function
- using Hasher = HASH;
- /// The equality function
- using Equality = EQUAL;
-
- /// Entry is used as the entry to the unordered_set
- struct Entry {
- /// The pre-calculated hash of the entry
- size_t hash;
- /// The pointer to the unique object
- T* ptr;
- };
- /// Comparator is the hashing and equality function used by the unordered_set
- struct Comparator {
+ /// Comparator is the hashing function used by the Hashset
+ struct Hasher {
/// Hashing function
/// @param e the entry
/// @returns the hash of the entry
- size_t operator()(Entry e) const { return e.hash; }
+ size_t operator()(T* e) const { return HASH{}(*e); }
+ };
+ /// Equality is the equality function used by the Hashset
+ struct Equality {
/// Equality function
/// @param a the first entry to compare
/// @param b the second entry to compare
/// @returns true if the two entries are equal
- bool operator()(Entry a, Entry b) const { return EQUAL{}(*a.ptr, *b.ptr); }
+ bool operator()(T* a, T* b) const { return EQUAL{}(*a, *b); }
+ };
+
+ /// The maximum capacity of Set before it performs heap allocations.
+ static constexpr size_t kFixedSize = 32;
+
+ /// The key type of Set.
+ using Key = HashmapKey<T*, Hasher, Equality>;
+
+ /// A custom Hashset implementation that allows keys to be modified.
+ class Set : public HashmapBase<Key, kFixedSize> {
+ public:
+ Key& Add(T* key) {
+ auto idx = this->EditAt(key);
+ if (!idx.entry) {
+ idx.Insert(std::forward<T*>(key));
+ }
+ return *idx.entry;
+ }
+
+ T* Get(T* key) const {
+ if (auto* entry = this->GetEntry(key)) {
+ return *entry;
+ }
+ return nullptr;
+ }
};
/// The block allocator used to allocate the unique objects
BlockAllocator<T> allocator;
- /// The unordered_set of unique item entries
- std::unordered_set<Entry, Comparator, Comparator> items;
+ /// The set of unique item entries
+ Set items;
};
} // namespace tint