tmp
This commit is contained in:
parent
a7b9feaedc
commit
0f15c37c2e
10 changed files with 522 additions and 1 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -22,3 +22,9 @@
|
|||
[submodule "Externals/implot"]
|
||||
path = Externals/implot
|
||||
url = https://github.com/epezent/implot.git
|
||||
[submodule "Externals/googletest"]
|
||||
path = Externals/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "Externals/benchmark"]
|
||||
path = Externals/benchmark
|
||||
url = https://github.com/google/benchmark.git
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
project(Containers)
|
||||
|
||||
include(GoogleTest)
|
||||
|
||||
### ---------------------- Static Library --------------------- ###
|
||||
file(GLOB SOURCES "./private/*.cpp")
|
||||
file(GLOB HEADERS "./public/*.hpp")
|
||||
|
|
@ -25,5 +27,10 @@ target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
|
|||
add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})
|
||||
|
||||
|
||||
add_executable(testLinearRingBuffer ./tests/testLinearRingBuffer.cpp)
|
||||
target_link_libraries(testLinearRingBuffer ${PROJECT_NAME} gtest)
|
||||
gtest_discover_tests(testLinearRingBuffer)
|
||||
|
||||
|
||||
add_executable(AVLTreeSpeedTest ./tests/AVLTreeProfiling.cpp)
|
||||
target_link_libraries(AVLTreeSpeedTest ${PROJECT_NAME})
|
||||
|
|
|
|||
65
Containers/private/linear_ring.cpp
Normal file
65
Containers/private/linear_ring.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include <cstdint>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
namespace lrbm {
|
||||
static int create_memfd(const char* name) {
|
||||
int fd = syscall(SYS_memfd_create, name, 0);
|
||||
if (fd == -1) throw std::runtime_error("memfd_create failed");
|
||||
return fd;
|
||||
}
|
||||
|
||||
std::pair<void*, size_t> create_linear_ring(size_t size) {
|
||||
const size_t page = sysconf(_SC_PAGESIZE);
|
||||
|
||||
size_t aligned = (size + page - 1) & ~(page - 1);
|
||||
|
||||
// Create RAM-backed file descriptor
|
||||
int fd = create_memfd("linear_ring");
|
||||
if (ftruncate(fd, aligned) != 0) throw std::runtime_error("ftruncate failed");
|
||||
|
||||
// Reserve 2× VA space
|
||||
uint8_t* base = (uint8_t*) mmap(nullptr, aligned * 2, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (base == MAP_FAILED) throw std::runtime_error("mmap reserve failed");
|
||||
|
||||
// First mapping
|
||||
void* p1 = mmap(base, aligned, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
|
||||
if (p1 != base) throw std::runtime_error("first mmap failed");
|
||||
|
||||
// Mirror mapping
|
||||
void* p2 = mmap(base + aligned, aligned, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
|
||||
if (p2 != base + aligned) throw std::runtime_error("mirror mmap failed");
|
||||
|
||||
close(fd);
|
||||
return { base, aligned }; // linear region [buf][buf]
|
||||
}
|
||||
|
||||
void destroy_linear_ring(void* base, size_t size) { munmap(base, size); }
|
||||
|
||||
// void test() {
|
||||
// const size_t RING_SIZE = 4096;
|
||||
// uint8_t* ring = (uint8_t*) create_linear_ring(RING_SIZE);
|
||||
|
||||
// printf("Linear ring created at %p (size = %zu)\n", ring, RING_SIZE);
|
||||
|
||||
// const char* msg = "HELLO_LINEAR_RING";
|
||||
// size_t msg_len = strlen(msg);
|
||||
|
||||
// size_t head = RING_SIZE - 4; // force boundary crossing
|
||||
// std::memcpy(ring + head, msg, msg_len);
|
||||
|
||||
// char out[64];
|
||||
// std::memcpy(out, ring + head, msg_len);
|
||||
// out[msg_len] = 0;
|
||||
|
||||
// printf("Expected: %s\n", msg);
|
||||
// printf("Readback: %s\n", out);
|
||||
|
||||
// if (std::strcmp(msg, out) == 0) printf("SUCCESS: Linear-address ring works!\n");
|
||||
// else printf("FAILURE: Ring broken!\n");
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "liniar_ring.hpp"
|
||||
|
||||
class LinearRingBuffer {
|
||||
public:
|
||||
LinearRingBuffer(size_t size, size_t history) {
|
||||
const auto& [mem, aligned_size] = lrbm::create_linear_ring(size);
|
||||
|
||||
mem_ = (uint8_t*) mem;
|
||||
memSize_ = aligned_size;
|
||||
bufferSize_ = size;
|
||||
historySize_ = history;
|
||||
|
||||
write_ptr_ = mem_;
|
||||
read_ptr_ = mem_;
|
||||
}
|
||||
|
||||
~LinearRingBuffer() { lrbm::destroy_linear_ring((void*) mem_, memSize_ * 2); }
|
||||
|
||||
uint8_t* write_data() { return write_ptr_; }
|
||||
uint8_t* read_data() { return read_ptr_; }
|
||||
|
||||
void write_advance(size_t bytes) { write_ptr_ = advancePointer(write_ptr_, bytes); }
|
||||
void read_advance(size_t bytes) { read_ptr_ = advancePointer(read_ptr_, bytes); }
|
||||
|
||||
void read_reset_with_history() {
|
||||
const auto offset = size_t(write_ptr_ - mem_);
|
||||
|
||||
if (historySize_ < offset) {
|
||||
read_ptr_ = write_ptr_ - historySize_;
|
||||
return;
|
||||
}
|
||||
|
||||
read_ptr_ = mem_ + (memSize_ + offset - historySize_);
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint8_t* advancePointer(const uint8_t* ptr, size_t bytes) {
|
||||
return mem_ + (size_t(ptr - mem_) + bytes) % memSize_;
|
||||
}
|
||||
|
||||
size_t bufferSize_ = 0;
|
||||
size_t historySize_ = 0;
|
||||
size_t memSize_ = 0;
|
||||
|
||||
uint8_t* mem_ = nullptr;
|
||||
|
||||
uint8_t* read_ptr_ = 0;
|
||||
uint8_t* write_ptr_ = 0;
|
||||
};
|
||||
9
Containers/public/liniar_ring.hpp
Normal file
9
Containers/public/liniar_ring.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace lrbm {
|
||||
std::pair<void*, size_t> create_linear_ring(size_t size);
|
||||
void destroy_linear_ring(void* base, size_t size);
|
||||
void test();
|
||||
}
|
||||
188
Containers/tests/benchLinearRingBuffer.cpp
Normal file
188
Containers/tests/benchLinearRingBuffer.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
#include "LinearRingBuffer.hpp"
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (historySize > prev.size()) historySize = prev.size();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
188
Containers/tests/testLinearRingBuffer.cpp
Normal file
188
Containers/tests/testLinearRingBuffer.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
#include "LinearRingBuffer.hpp"
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (historySize > prev.size()) historySize = prev.size();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
2
Externals/CMakeLists.txt
vendored
2
Externals/CMakeLists.txt
vendored
|
|
@ -54,3 +54,5 @@ add_library(${PROJECT_NAME} INTERFACE)
|
|||
target_include_directories(${PROJECT_NAME} INTERFACE ./imageIO/)
|
||||
|
||||
add_subdirectory(lalr)
|
||||
|
||||
add_subdirectory(googletest)
|
||||
|
|
|
|||
1
Externals/benchmark
vendored
Submodule
1
Externals/benchmark
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit c3f86578bb2081b52cee9d51615912ca4aa52fe4
|
||||
1
Externals/googletest
vendored
Submodule
1
Externals/googletest
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1b96fa13f549387b7549cc89e1a785cf143a1a50
|
||||
Loading…
Add table
Add a link
Reference in a new issue