LibGC: Amortize pruning of dead entries in WeakHashSet
Previously, every set() and remove() call would do a full O(capacity) scan of the underlying hash table to remove dead weak references. This caused significant stalls on pages where the set grew large, as every insertion paid the full cost of scanning the entire table. Fix this by tracking mutations since last prune and only performing the scan after enough operations to amortize the cost. The threshold scales with the table size (minimum 64), so the per-operation cost is O(1) amortized.
This commit is contained in:
parent
12af95cfa2
commit
fc675e8634
1 changed files with 7 additions and 3 deletions
|
|
@ -27,13 +27,13 @@ public:
|
|||
|
||||
void set(T& value)
|
||||
{
|
||||
prune_dead_entries();
|
||||
maybe_prune();
|
||||
m_table.set(Weak<T>(value));
|
||||
}
|
||||
|
||||
bool remove(T& value)
|
||||
{
|
||||
prune_dead_entries();
|
||||
maybe_prune();
|
||||
auto it = m_table.find(Traits<T*>::hash(&value), [&](auto& entry) { return entry.ptr() == &value; });
|
||||
if (it == m_table.end())
|
||||
return false;
|
||||
|
|
@ -139,14 +139,18 @@ public:
|
|||
ConstIterator end() const { return ConstIterator(m_table.end(), m_table.end()); }
|
||||
|
||||
private:
|
||||
void prune_dead_entries()
|
||||
void maybe_prune()
|
||||
{
|
||||
if (++m_mutations_since_last_prune < max(m_table.size(), static_cast<size_t>(64)))
|
||||
return;
|
||||
m_table.remove_all_matching([](Weak<T> const& entry) {
|
||||
return !entry.ptr();
|
||||
});
|
||||
m_mutations_since_last_prune = 0;
|
||||
}
|
||||
|
||||
TableType m_table;
|
||||
size_t m_mutations_since_last_prune { 0 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue