Merge pull request #108932 from Nazarwadim/hash_map_final_optimization

Do not zero elements and perform fast clear in `HashMap`
This commit is contained in:
Thaddeus Crews
2025-11-17 10:56:27 -06:00

View File

@ -181,8 +181,9 @@ private:
_size = 0; _size = 0;
static_assert(EMPTY_HASH == 0, "Assuming EMPTY_HASH = 0 for alloc_static_zeroed call"); static_assert(EMPTY_HASH == 0, "Assuming EMPTY_HASH = 0 for alloc_static_zeroed call");
_hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static_zeroed(sizeof(uint32_t) * capacity)); _hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static_zeroed(sizeof(uint32_t) * capacity));
_elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static_zeroed(sizeof(HashMapElement<TKey, TValue> *) * capacity)); _elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static(sizeof(HashMapElement<TKey, TValue> *) * capacity));
if (old_capacity == 0) { if (old_capacity == 0) {
// Nothing to do. // Nothing to do.
@ -208,7 +209,7 @@ private:
static_assert(EMPTY_HASH == 0, "Assuming EMPTY_HASH = 0 for alloc_static_zeroed call"); static_assert(EMPTY_HASH == 0, "Assuming EMPTY_HASH = 0 for alloc_static_zeroed call");
_hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static_zeroed(sizeof(uint32_t) * capacity)); _hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static_zeroed(sizeof(uint32_t) * capacity));
_elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static_zeroed(sizeof(HashMapElement<TKey, TValue> *) * capacity)); _elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static(sizeof(HashMapElement<TKey, TValue> *) * capacity));
} }
if (_size + 1 > MAX_OCCUPANCY * capacity) { if (_size + 1 > MAX_OCCUPANCY * capacity) {
@ -235,6 +236,15 @@ private:
return elem; return elem;
} }
void _clear_data() {
HashMapElement<TKey, TValue> *current = _tail_element;
while (current != nullptr) {
HashMapElement<TKey, TValue> *prev = current->prev;
Allocator::delete_allocation(current);
current = prev;
}
}
public: public:
_FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[_capacity_idx]; } _FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[_capacity_idx]; }
_FORCE_INLINE_ uint32_t size() const { return _size; } _FORCE_INLINE_ uint32_t size() const { return _size; }
@ -249,16 +259,9 @@ public:
if (_elements == nullptr || _size == 0) { if (_elements == nullptr || _size == 0) {
return; return;
} }
uint32_t capacity = hash_table_size_primes[_capacity_idx];
for (uint32_t i = 0; i < capacity; i++) {
if (_hashes[i] == EMPTY_HASH) {
continue;
}
_hashes[i] = EMPTY_HASH; _clear_data();
Allocator::delete_allocation(_elements[i]); memset(_hashes, EMPTY_HASH, get_capacity() * sizeof(uint32_t));
_elements[i] = nullptr;
}
_tail_element = nullptr; _tail_element = nullptr;
_head_element = nullptr; _head_element = nullptr;
@ -356,7 +359,6 @@ public:
} }
Allocator::delete_allocation(_elements[idx]); Allocator::delete_allocation(_elements[idx]);
_elements[idx] = nullptr;
_size--; _size--;
return true; return true;
@ -385,8 +387,9 @@ public:
idx = next_idx; idx = next_idx;
_increment_mod(next_idx, capacity); _increment_mod(next_idx, capacity);
} }
_hashes[idx] = EMPTY_HASH; _hashes[idx] = EMPTY_HASH;
_elements[idx] = nullptr;
// _insert_element will increment this again. // _insert_element will increment this again.
_size--; _size--;
@ -688,7 +691,7 @@ public:
} }
~HashMap() { ~HashMap() {
clear(); _clear_data();
if (_elements != nullptr) { if (_elements != nullptr) {
Memory::free_static(_elements); Memory::free_static(_elements);