tmp
This commit is contained in:
parent
0f15c37c2e
commit
10c1566c6c
4 changed files with 96 additions and 176 deletions
|
|
@ -32,5 +32,9 @@ target_link_libraries(testLinearRingBuffer ${PROJECT_NAME} gtest)
|
||||||
gtest_discover_tests(testLinearRingBuffer)
|
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)
|
add_executable(AVLTreeSpeedTest ./tests/AVLTreeProfiling.cpp)
|
||||||
target_link_libraries(AVLTreeSpeedTest ${PROJECT_NAME})
|
target_link_libraries(AVLTreeSpeedTest ${PROJECT_NAME})
|
||||||
|
|
|
||||||
|
|
@ -1,188 +1,50 @@
|
||||||
#include "LinearRingBuffer.hpp"
|
#include <benchmark/benchmark.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
// -------------------------------------------------------------
|
||||||
|
// Benchmark: push_back into a vector
|
||||||
#include <cstring>
|
// -------------------------------------------------------------
|
||||||
#include <cstdint>
|
static void BM_VectorPushBack(benchmark::State& state) {
|
||||||
#include <random>
|
for (auto _ : state) {
|
||||||
|
std::vector<int> v;
|
||||||
static void fill(void* ptr, size_t n, uint8_t value) { std::memset(ptr, value, n); }
|
v.reserve(state.range(0));
|
||||||
|
for (int i = 0; i < state.range(0); i++) {
|
||||||
static bool check(const void* ptr, size_t n, uint8_t value) {
|
v.push_back(i);
|
||||||
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;
|
benchmark::DoNotOptimize(v);
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memcpy(this->rb.write_data(), pushedSample.data(), pushedSample.size());
|
|
||||||
this->rb.write_advance(pushedSample.size());
|
|
||||||
|
|
||||||
if (storage) {
|
|
||||||
storage->push_back(pushedSample);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Data makeMessageWithHistory(const Data& prev, const Data& current, size_t historySize) {
|
BENCHMARK(BM_VectorPushBack)->Arg(1000)->Arg(100000);
|
||||||
if (historySize > prev.size()) historySize = prev.size();
|
|
||||||
|
|
||||||
std::vector<uint8_t> result;
|
// -------------------------------------------------------------
|
||||||
|
// Benchmark: inserting into std::map
|
||||||
result.reserve(historySize + current.size());
|
// -------------------------------------------------------------
|
||||||
|
static void BM_MapInsert(benchmark::State& state) {
|
||||||
result.insert(result.end(), prev.end() - historySize, prev.end());
|
for (auto _ : state) {
|
||||||
result.insert(result.end(), current.begin(), current.end());
|
std::map<int, int> m;
|
||||||
|
for (int i = 0; i < state.range(0); i++) {
|
||||||
return result;
|
m.emplace(i, i);
|
||||||
}
|
}
|
||||||
|
benchmark::DoNotOptimize(m);
|
||||||
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_MapInsert)->Arg(1000)->Arg(100000);
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
// -------------------------------------------------------------
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
// Benchmark: lookup in std::map
|
||||||
return RUN_ALL_TESTS();
|
// -------------------------------------------------------------
|
||||||
|
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);
|
||||||
|
|
||||||
|
for (auto _ : state) {
|
||||||
|
auto it = m.find(state.range(0) / 2);
|
||||||
|
benchmark::DoNotOptimize(it);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_MapLookup)->Arg(1000)->Arg(100000);
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
|
|
|
||||||
1
Externals/CMakeLists.txt
vendored
1
Externals/CMakeLists.txt
vendored
|
|
@ -56,3 +56,4 @@ target_include_directories(${PROJECT_NAME} INTERFACE ./imageIO/)
|
||||||
add_subdirectory(lalr)
|
add_subdirectory(lalr)
|
||||||
|
|
||||||
add_subdirectory(googletest)
|
add_subdirectory(googletest)
|
||||||
|
add_subdirectory(benchmark)
|
||||||
|
|
|
||||||
53
tools/gen_bench_image.py
Normal file
53
tools/gen_bench_image.py
Normal 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()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue