From 68035fbd98889a2f7fb39bf365c6ba7cf5a643e4 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Wed, 28 Jan 2026 13:57:17 +0100 Subject: [PATCH] =?UTF-8?q?AK:=20Make=20Vector::remove=5Fall=5Fmatching=20?= =?UTF-8?q?O(n)=20instead=20of=20O(n=C2=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If multiple entries match during a single run of Vector::remove_all_matching(), the contents of the vector would shift back multiple times as well. This provides a new approach that iterates only once over each entry. Improves the provided benchmark case (running 1000 iterations) from ~900ms to ~8ms on my machine. --- AK/Vector.h | 25 +++++++++++++++++-------- Tests/AK/TestVector.cpp | 10 ++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/AK/Vector.h b/AK/Vector.h index 6160ee1a25..4deaaa52f7 100644 --- a/AK/Vector.h +++ b/AK/Vector.h @@ -590,16 +590,25 @@ public: template bool remove_all_matching(TUnaryPredicate const& predicate) { - bool something_was_removed = false; - for (size_t i = 0; i < size();) { - if (predicate(at(i))) { - remove(i); - something_was_removed = true; - } else { - ++i; + size_t write_index = 0; + for (size_t read_index = 0; read_index < m_size; ++read_index) { + if (predicate(at(read_index))) { + TypedTransfer::delete_(slot(read_index), 1); + continue; } + if (read_index != write_index) { + TypedTransfer::move(slot(write_index), slot(read_index), 1); + TypedTransfer::delete_(slot(read_index), 1); + } + ++write_index; } - return something_was_removed; + + if (write_index == m_size) + return false; + + m_size = write_index; + update_metadata(); + return true; } ALWAYS_INLINE T take_last() diff --git a/Tests/AK/TestVector.cpp b/Tests/AK/TestVector.cpp index c779d673cc..8bf812933d 100644 --- a/Tests/AK/TestVector.cpp +++ b/Tests/AK/TestVector.cpp @@ -247,6 +247,16 @@ static bool is_inline_element(auto& el, auto& vector) EXPECT_EQ(ints.size(), 0u); \ } \ \ + BENCHMARK_CASE(Vector##_remove_all_matching_trivial) \ + { \ + Vector ints; \ + for (int i = 0; i < 10000; ++i) { \ + ints.append(i); \ + } \ + ints.remove_all_matching([](int value) { return value % 2 == 0; }); \ + EXPECT_EQ(ints.size(), 5000u); \ + } \ + \ TEST_CASE(Vector##_vector_remove) \ { \ Vector ints; \