diff --git a/Containers/CMakeLists.txt b/Containers/CMakeLists.txt index dcdcff9..146ae01 100644 --- a/Containers/CMakeLists.txt +++ b/Containers/CMakeLists.txt @@ -32,9 +32,5 @@ target_link_libraries(testLinearRingBuffer ${PROJECT_NAME} gtest) gtest_discover_tests(testLinearRingBuffer) -add_executable(benchLinearRingBuffer ./tests/benchLinearRingBuffer.cpp) -target_link_libraries(benchLinearRingBuffer benchmark::benchmark benchmark::benchmark_main ${PROJECT_NAME}) -# target_compile_features(benchLinearRingBuffer PRIVATE cxx_std_20) - add_executable(AVLTreeSpeedTest ./tests/AVLTreeProfiling.cpp) target_link_libraries(AVLTreeSpeedTest ${PROJECT_NAME}) diff --git a/Containers/tests/benchLinearRingBuffer.cpp b/Containers/tests/benchLinearRingBuffer.cpp index 1c6fa43..a9293f3 100644 --- a/Containers/tests/benchLinearRingBuffer.cpp +++ b/Containers/tests/benchLinearRingBuffer.cpp @@ -1,50 +1,188 @@ -#include -#include -#include +#include "LinearRingBuffer.hpp" -// ------------------------------------------------------------- -// Benchmark: push_back into a vector -// ------------------------------------------------------------- -static void BM_VectorPushBack(benchmark::State& state) { - for (auto _ : state) { - std::vector v; - v.reserve(state.range(0)); - for (int i = 0; i < state.range(0); i++) { - v.push_back(i); +#include + +#include +#include +#include + +static void fill(void* ptr, size_t n, uint8_t value) { std::memset(ptr, value, n); } + +static bool check(const void* ptr, size_t n, uint8_t value) { + const uint8_t* p = static_cast(ptr); + for (size_t i = 0; i < n; ++i) { + if (p[i] != value) return false; + } + return true; +} + +static bool expect_mem_eq(const void* ptr, size_t n, uint8_t value) { + const uint8_t* p = static_cast(ptr); + for (size_t i = 0; i < n; ++i) { + if (p[i] != value) return false; + } + return true; +} + +using Data = std::vector; +using DataStorage = std::vector; + +typedef ::testing::Types< + std::integral_constant, + std::integral_constant, + std::integral_constant, + std::integral_constant, + std::integral_constant> + BufferSizes; + +template +class LinearRingBufferTest : public ::testing::Test { +protected: + LinearRingBuffer rb; + + LinearRingBufferTest() : + historySize_(10), + rb(size_t(TypeParam::value), size_t(historySize_)) {} + + static constexpr auto ITERATIONS = TypeParam::value * 10; + + size_t messagesPushed_ = 0; + size_t historySize_ = 0; + + auto iterations() { return ITERATIONS; } + + auto historySize() { return historySize_; } + + size_t randomSize() { + static std::random_device rnd; + std::uniform_int_distribution dist(0, 1000); + return dist(rnd); + } + + auto write_and_advance(size_t messageSize, DataStorage* storage) { + Data pushedSample(messageSize); + + for (auto idx = 0; idx < messageSize; idx++) { + pushedSample[idx] = (idx + messagesPushed_++ * 53) % 321; } - benchmark::DoNotOptimize(v); - } -} -BENCHMARK(BM_VectorPushBack)->Arg(1000)->Arg(100000); + std::memcpy(this->rb.write_data(), pushedSample.data(), pushedSample.size()); + this->rb.write_advance(pushedSample.size()); -// ------------------------------------------------------------- -// Benchmark: inserting into std::map -// ------------------------------------------------------------- -static void BM_MapInsert(benchmark::State& state) { - for (auto _ : state) { - std::map m; - for (int i = 0; i < state.range(0); i++) { - m.emplace(i, i); + if (storage) { + storage->push_back(pushedSample); } - benchmark::DoNotOptimize(m); + }; + + auto read_and_advance(size_t messageSize, DataStorage* storage) { + Data poppedSample(messageSize); + + std::memcpy(poppedSample.data(), this->rb.read_data(), poppedSample.size()); + + this->rb.read_advance(poppedSample.size()); + + if (storage) { + storage->push_back(poppedSample); + } + }; + + auto reset_with_history() { this->rb.read_reset_with_history(); }; + + auto read_with_history(size_t messageSize, DataStorage* storage) { + Data poppedSample(messageSize); + + std::memcpy(poppedSample.data(), this->rb.read_data(), poppedSample.size()); + + if (storage) { + storage->push_back(poppedSample); + } + }; +}; + +TYPED_TEST_SUITE(LinearRingBufferTest, BufferSizes); + +TYPED_TEST(LinearRingBufferTest, LinearWriteAcrossBoundary) { + constexpr size_t N = TypeParam::value; + + if (N % 4096 != 0) { + return; + } + + auto* base = this->rb.write_data(); + + size_t half = N - 16; + fill(base, half, 0xAA); + fill(base + half, 32, 0xBB); + + EXPECT_EQ(expect_mem_eq(base, 16, 0xBB), true); + EXPECT_EQ(expect_mem_eq(base + 16, half - 16, 0xAA), true); + EXPECT_EQ(expect_mem_eq(base + half, 32, 0xBB), true); +} + +TYPED_TEST(LinearRingBufferTest, Chase) { + + DataStorage pushed; + DataStorage popped; + + static constexpr auto MSG_SIZE = 31; + + for (auto iteration = 0; iteration < this->iterations(); iteration++) { + this->write_and_advance(MSG_SIZE, &pushed); + this->read_and_advance(MSG_SIZE, &popped); + } + + ASSERT_EQ(pushed.size(), popped.size()); + + for (auto idx = 0; idx < popped.size(); idx++) { + ASSERT_EQ(pushed[idx], popped[idx]); } } -BENCHMARK(BM_MapInsert)->Arg(1000)->Arg(100000); -// ------------------------------------------------------------- -// Benchmark: lookup in std::map -// ------------------------------------------------------------- -static void BM_MapLookup(benchmark::State& state) { - std::map m; - for (int i = 0; i < state.range(0); i++) - m.emplace(i, i); +Data makeMessageWithHistory(const Data& prev, const Data& current, size_t historySize) { + if (historySize > prev.size()) historySize = prev.size(); - for (auto _ : state) { - auto it = m.find(state.range(0) / 2); - benchmark::DoNotOptimize(it); + std::vector result; + + result.reserve(historySize + current.size()); + + result.insert(result.end(), prev.end() - historySize, prev.end()); + result.insert(result.end(), current.begin(), current.end()); + + return result; +} + +TYPED_TEST(LinearRingBufferTest, History) { + + DataStorage pushed; + DataStorage popped; + + pushed.push_back(Data(this->historySize())); + + static constexpr auto MAX_MSG_SIZE = 111; + + for (auto iteration = 0; iteration < this->iterations(); iteration++) { + + const auto MSG_SIZE = this->historySize() + this->randomSize() % MAX_MSG_SIZE; + + auto prevPushed = pushed.back(); + pushed.clear(); + + this->reset_with_history(); + + this->write_and_advance(MSG_SIZE, &pushed); + auto currentPushed = pushed.back(); + + this->read_with_history(this->historySize() + MSG_SIZE, &popped); + auto poppedWithHistory = popped.back(); + popped.clear(); + + auto correctResult = makeMessageWithHistory(prevPushed, currentPushed, this->historySize()); + + ASSERT_EQ(correctResult, poppedWithHistory); } } -BENCHMARK(BM_MapLookup)->Arg(1000)->Arg(100000); -BENCHMARK_MAIN(); +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Externals/CMakeLists.txt b/Externals/CMakeLists.txt index 3cf7daa..ec85993 100644 --- a/Externals/CMakeLists.txt +++ b/Externals/CMakeLists.txt @@ -56,4 +56,3 @@ target_include_directories(${PROJECT_NAME} INTERFACE ./imageIO/) add_subdirectory(lalr) add_subdirectory(googletest) -add_subdirectory(benchmark) diff --git a/tools/gen_bench_image.py b/tools/gen_bench_image.py deleted file mode 100644 index cd82078..0000000 --- a/tools/gen_bench_image.py +++ /dev/null @@ -1,53 +0,0 @@ -import json -import sys -import re -import matplotlib.pyplot as plt - -def load_bench(path): - with open(path, "r") as f: - return json.load(f)["benchmarks"] - -def parse_arg_from_name(name): - # Example: "BM_VectorPushBack/1000" -> 1000 - m = re.search(r"/(\d+)$", name) - return int(m.group(1)) if m else None - -def group_by_prefix(benchmarks): - groups = {} - for b in benchmarks: - name = b["name"] - prefix = name.split("/")[0] # BM_VectorPushBack - arg = parse_arg_from_name(name) # number after slash - if arg is None: - continue - if prefix not in groups: - groups[prefix] = {"x": [], "y": []} - groups[prefix]["x"].append(arg) - groups[prefix]["y"].append(b["real_time"]) - return groups - -def main(): - if len(sys.argv) != 2: - print("Usage: python3 plot_bench.py results.json") - return - - benchmarks = load_bench(sys.argv[1]) - groups = group_by_prefix(benchmarks) - - plt.figure(figsize=(10, 6)) - - for prefix, data in groups.items(): - xs = data["x"] - ys = data["y"] - plt.plot(xs, ys, marker="o", label=prefix) - - plt.xlabel("Input size (Arg)") - plt.ylabel("Real time (ns)") - plt.title("Google Benchmark Results") - plt.grid(True) - plt.legend() - plt.tight_layout() - plt.show() - -if __name__ == "__main__": - main()