Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Шурупов Илья Викторович
10c1566c6c tmp 2026-05-14 20:37:09 +03:00
4 changed files with 96 additions and 176 deletions

View file

@ -32,5 +32,9 @@ 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})

View file

@ -1,188 +1,50 @@
#include "LinearRingBuffer.hpp"
#include <benchmark/benchmark.h>
#include <vector>
#include <map>
#include <gtest/gtest.h>
#include <cstring>
#include <cstdint>
#include <random>
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<const uint8_t*>(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<const uint8_t*>(ptr);
for (size_t i = 0; i < n; ++i) {
if (p[i] != value) return false;
}
return true;
}
using Data = std::vector<uint8_t>;
using DataStorage = std::vector<Data>;
typedef ::testing::Types<
std::integral_constant<size_t, 64>,
std::integral_constant<size_t, 128>,
std::integral_constant<size_t, 1000>,
std::integral_constant<size_t, 4096>,
std::integral_constant<size_t, 4096 * 2>>
BufferSizes;
template <typename TypeParam>
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<size_t> 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: push_back into a vector
// -------------------------------------------------------------
static void BM_VectorPushBack(benchmark::State& state) {
for (auto _ : state) {
std::vector<int> v;
v.reserve(state.range(0));
for (int i = 0; i < state.range(0); i++) {
v.push_back(i);
}
benchmark::DoNotOptimize(v);
}
}
std::memcpy(this->rb.write_data(), pushedSample.data(), pushedSample.size());
this->rb.write_advance(pushedSample.size());
BENCHMARK(BM_VectorPushBack)->Arg(1000)->Arg(100000);
if (storage) {
storage->push_back(pushedSample);
// -------------------------------------------------------------
// Benchmark: inserting into std::map
// -------------------------------------------------------------
static void BM_MapInsert(benchmark::State& state) {
for (auto _ : state) {
std::map<int, int> m;
for (int i = 0; i < state.range(0); i++) {
m.emplace(i, i);
}
};
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::DoNotOptimize(m);
}
}
BENCHMARK(BM_MapInsert)->Arg(1000)->Arg(100000);
Data makeMessageWithHistory(const Data& prev, const Data& current, size_t historySize) {
if (historySize > prev.size()) historySize = prev.size();
// -------------------------------------------------------------
// Benchmark: lookup in std::map
// -------------------------------------------------------------
static void BM_MapLookup(benchmark::State& state) {
std::map<int, int> m;
for (int i = 0; i < state.range(0); i++)
m.emplace(i, i);
std::vector<uint8_t> 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);
for (auto _ : state) {
auto it = m.find(state.range(0) / 2);
benchmark::DoNotOptimize(it);
}
}
BENCHMARK(BM_MapLookup)->Arg(1000)->Arg(100000);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
BENCHMARK_MAIN();

View file

@ -56,3 +56,4 @@ target_include_directories(${PROJECT_NAME} INTERFACE ./imageIO/)
add_subdirectory(lalr)
add_subdirectory(googletest)
add_subdirectory(benchmark)

53
tools/gen_bench_image.py Normal file
View file

@ -0,0 +1,53 @@
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()