mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
AK: Automatically shrink HashTable when removing entries
If the utilization of a HashTable (size vs capacity) goes below 20%, we'll now shrink the table down to capacity = (size * 2). This fixes an issue where tables would grow infinitely when inserting and removing keys repeatedly. Basically, we would accumulate deleted buckets with nothing reclaiming them, and eventually deciding that we needed to grow the table (because we grow if used+deleted > limit!) I found this because HashTable iteration was taking a suspicious amount of time in Core::EventLoop::get_next_timer_expiration(). Turns out the timer table kept growing in capacity over time. That made iteration slower and slower since HashTable iterators visit every bucket.
This commit is contained in:
parent
eb829924da
commit
9d8da1697e
Notes:
sideshowbarker
2024-07-17 17:51:15 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/9d8da1697e
1 changed files with 17 additions and 2 deletions
|
@ -402,6 +402,8 @@ public:
|
|||
delete_bucket(bucket);
|
||||
--m_size;
|
||||
++m_deleted_count;
|
||||
|
||||
shrink_if_needed();
|
||||
}
|
||||
|
||||
template<typename TUnaryPredicate>
|
||||
|
@ -418,9 +420,9 @@ public:
|
|||
if (removed_count) {
|
||||
m_deleted_count += removed_count;
|
||||
m_size -= removed_count;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
shrink_if_needed();
|
||||
return removed_count;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -543,6 +545,19 @@ private:
|
|||
[[nodiscard]] size_t used_bucket_count() const { return m_size + m_deleted_count; }
|
||||
[[nodiscard]] bool should_grow() const { return ((used_bucket_count() + 1) * 100) >= (m_capacity * load_factor_in_percent); }
|
||||
|
||||
void shrink_if_needed()
|
||||
{
|
||||
// Shrink if less than 20% of buckets are used, but never going below 16.
|
||||
// These limits are totally arbitrary and can probably be improved.
|
||||
bool should_shrink = m_size * 5 < m_capacity && m_capacity > 16;
|
||||
if (!should_shrink)
|
||||
return;
|
||||
|
||||
// NOTE: We ignore memory allocation failure here, since we can continue
|
||||
// just fine with an oversized table.
|
||||
(void)try_rehash(m_size * 2);
|
||||
}
|
||||
|
||||
void delete_bucket(auto& bucket)
|
||||
{
|
||||
bucket.slot()->~T();
|
||||
|
|
Loading…
Reference in a new issue