LibGC: Make MarkingVisitor better at bulk-visiting Vector<JS::Value>

When passing a Vector<JS::Value> to the MarkingVisitor, we were
iterating over the vector and visiting one value at a time. This led
to a very inefficient way of building up the GC's work queue.

By adding a new visit_impl() virtual to Cell::Visitor, we can now
grow the work queue capacity once, and then add without incrementally
growing the storage.
This commit is contained in:
Andreas Kling 2026-01-07 14:22:51 +01:00 committed by Andreas Kling
parent 2ac363dcba
commit 8b19992f8c
3 changed files with 54 additions and 0 deletions

View file

@ -16,6 +16,7 @@
#include <AK/Weakable.h>
#include <LibGC/Forward.h>
#include <LibGC/Internals.h>
#include <LibGC/NanBoxedValue.h>
#include <LibGC/Ptr.h>
namespace GC {
@ -103,6 +104,13 @@ public:
visit(value);
}
template<typename T>
void visit(ReadonlySpan<T> span)
requires(IsBaseOf<NanBoxedValue, T>)
{
visit_impl(ReadonlySpan<NanBoxedValue>(span.data(), span.size()));
}
template<typename T>
void visit(Span<T> span)
{
@ -110,6 +118,13 @@ public:
visit(value);
}
template<typename T>
void visit(Span<T> span)
requires(IsBaseOf<NanBoxedValue, T>)
{
visit_impl(ReadonlySpan<NanBoxedValue>(span.data(), span.size()));
}
template<typename T, size_t inline_capacity>
void visit(Vector<T, inline_capacity> const& vector)
{
@ -117,6 +132,13 @@ public:
visit(value);
}
template<typename T, size_t inline_capacity>
void visit(Vector<T, inline_capacity> const& vector)
requires(IsBaseOf<NanBoxedValue, T>)
{
visit_impl(ReadonlySpan<NanBoxedValue>(vector.span().data(), vector.size()));
}
template<typename T>
void visit(HashTable<T> const& table)
{
@ -173,6 +195,7 @@ public:
protected:
virtual void visit_impl(Cell&) = 0;
virtual void visit_impl(ReadonlySpan<NanBoxedValue>) = 0;
virtual ~Visitor() = default;
} SWIFT_UNSAFE_REFERENCE;

View file

@ -150,6 +150,12 @@ public:
m_work_queue.append(cell);
}
virtual void visit_impl(ReadonlySpan<NanBoxedValue> values) override
{
for (auto const& value : values)
visit(value);
}
virtual void visit_possible_values(ReadonlyBytes bytes) override
{
HashMap<FlatPtr, HeapRoot> possible_pointers;
@ -490,6 +496,23 @@ public:
m_work_queue.append(cell);
}
virtual void visit_impl(ReadonlySpan<NanBoxedValue> values) override
{
m_work_queue.ensure_capacity(m_work_queue.size() + values.size());
for (auto value : values) {
if (!value.is_cell())
continue;
auto& cell = value.as_cell();
if (cell.is_marked())
continue;
dbgln_if(HEAP_DEBUG, " ! {}", &cell);
cell.set_marked(true);
m_work_queue.unchecked_append(cell);
}
}
virtual void visit_possible_values(ReadonlyBytes bytes) override
{
HashMap<FlatPtr, HeapRoot> possible_pointers;

View file

@ -260,6 +260,14 @@ struct ExecutionContextRootsCollector : public Cell::Visitor {
roots.set(&cell);
}
virtual void visit_impl(ReadonlySpan<GC::NanBoxedValue> values) override
{
for (auto const& value : values) {
if (value.is_cell())
roots.set(value.as_cell());
}
}
virtual void visit_possible_values(ReadonlyBytes) override
{
VERIFY_NOT_REACHED();