From b1136ba3571731ac15e6b687f17a39160f761f2d Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Thu, 13 Apr 2023 15:47:12 +0200 Subject: [PATCH] AK: Efficiently resize CircularBuffer seekback copy distance Previously, if we copied the last byte for a length of 100, we'd recalculate the read span 100 times and memmove one byte 100 times, which resulted in a lot of overhead. Now, if we know that we have two consecutive copies of the data, we just extend the distance to cover both copies, which halves the number of times that we recalculate the span and actually call memmove. This takes the running time of the attached benchmark case from 150ms down to 15ms. --- AK/CircularBuffer.cpp | 8 +++++++- Tests/AK/TestCircularBuffer.cpp | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/AK/CircularBuffer.cpp b/AK/CircularBuffer.cpp index becef3c58fc..364e8da1094 100644 --- a/AK/CircularBuffer.cpp +++ b/AK/CircularBuffer.cpp @@ -220,7 +220,13 @@ ErrorOr CircularBuffer::copy_from_seekback(size_t distance, size_t lengt if (next_span.size() == 0) break; - remaining_length -= write(next_span.trim(remaining_length)); + auto length_written = write(next_span.trim(remaining_length)); + remaining_length -= length_written; + + // If we copied right from the end of the seekback area (i.e. our length is larger than the distance) + // and the last copy was one complete "chunk", we can now double the distance to copy twice as much data in one go. + if (remaining_length > distance && length_written == distance) + distance *= 2; } return length - remaining_length; diff --git a/Tests/AK/TestCircularBuffer.cpp b/Tests/AK/TestCircularBuffer.cpp index 5551d72d3ca..309bfbf2979 100644 --- a/Tests/AK/TestCircularBuffer.cpp +++ b/Tests/AK/TestCircularBuffer.cpp @@ -348,3 +348,18 @@ TEST_CASE(offset_of_with_until_and_after_wrapping_around) result = circular_buffer.offset_of("Well "sv, 14, 19); EXPECT_EQ(result.value_or(42), 14ul); } + +BENCHMARK_CASE(looping_copy_from_seekback) +{ + auto circular_buffer = MUST(CircularBuffer::create_empty(16 * MiB)); + + { + auto written_bytes = circular_buffer.write("\0"sv.bytes()); + EXPECT_EQ(written_bytes, 1ul); + } + + { + auto copied_bytes = TRY_OR_FAIL(circular_buffer.copy_from_seekback(1, 15 * MiB)); + EXPECT_EQ(copied_bytes, 15 * MiB); + } +}