diff --git a/lib/pls/CMakeLists.txt b/lib/pls/CMakeLists.txt index a3c9989..8b56cd0 100644 --- a/lib/pls/CMakeLists.txt +++ b/lib/pls/CMakeLists.txt @@ -7,7 +7,10 @@ add_library(pls STATIC src/internal/scheduling/abstract_task.cpp include/pls/internal/scheduling/abstract_task.h src/internal/scheduling/scheduler.cpp include/pls/internal/scheduling/scheduler.h src/internal/scheduling/thread_state.cpp include/pls/internal/scheduling/thread_state.h - src/internal/base/barrier.cpp include/pls/internal/base/barrier.h include/pls/internal/scheduling/root_master_task.h src/internal/scheduling/root_master_task.cpp) + src/internal/base/barrier.cpp include/pls/internal/base/barrier.h + src/internal/scheduling/root_master_task.cpp include/pls/internal/scheduling/root_master_task.h + src/internal/base/aligned_stack.cpp include/pls/internal/base/aligned_stack.h + include/pls/internal/base/system_details.h) # Add everything in `./include` to be in the include path of this project target_include_directories(pls diff --git a/lib/pls/include/pls/internal/base/aligned_stack.h b/lib/pls/include/pls/internal/base/aligned_stack.h new file mode 100644 index 0000000..f0c4774 --- /dev/null +++ b/lib/pls/include/pls/internal/base/aligned_stack.h @@ -0,0 +1,52 @@ + +#ifndef PLS_ALIGNED_STACK_H +#define PLS_ALIGNED_STACK_H + +#include +#include + +namespace pls { + namespace internal { + namespace base { + class aligned_stack { + // Keep bounds of our memory block + char* memory_start_; + char* memory_end_; + + // Current head will always be aligned to cache lines + char* head_; + + static std::uintptr_t next_alignment(std::uintptr_t size); + static char* next_alignment(char* pointer); + public: + explicit aligned_stack(char* memory_region, const std::size_t size): + memory_start_{memory_region}, + memory_end_{memory_region + size}, + head_{next_alignment(memory_start_)} {} + + template + T* push(T object) { + T* result = reinterpret_cast(head_); + + // Move head to next aligned position after new object + head_ = next_alignment(head_ + sizeof(T)); + if (head_ >= memory_end_) { + exit(1); // TODO: Exception Handling + } + + *result = object; + return result; + } + + template + T pop() { + head_ = head_ - next_alignment(sizeof(T)); + + return *reinterpret_cast(head_); + } + }; + } + } +} + +#endif //PLS_ALIGNED_STACK_H diff --git a/lib/pls/include/pls/internal/base/system_details.h b/lib/pls/include/pls/internal/base/system_details.h new file mode 100644 index 0000000..a8dfb72 --- /dev/null +++ b/lib/pls/include/pls/internal/base/system_details.h @@ -0,0 +1,15 @@ + +#ifndef PLS_SYSTEM_DETAILS_H +#define PLS_SYSTEM_DETAILS_H + +#include + +namespace pls { + namespace internal { + namespace base { + constexpr std::uintptr_t CACHE_LINE_SIZE = 64; + } + } +} + +#endif //PLS_SYSTEM_DETAILS_H diff --git a/lib/pls/src/internal/base/aligned_stack.cpp b/lib/pls/src/internal/base/aligned_stack.cpp new file mode 100644 index 0000000..4efe681 --- /dev/null +++ b/lib/pls/src/internal/base/aligned_stack.cpp @@ -0,0 +1,21 @@ +#include "pls/internal/base/aligned_stack.h" +#include "pls/internal/base/system_details.h" + +namespace pls { + namespace internal { + namespace base { + std::uintptr_t aligned_stack::next_alignment(std::uintptr_t size) { + std::uintptr_t miss_alignment = size % CACHE_LINE_SIZE; + if (miss_alignment == 0) { + return size; + } else { + return size + (CACHE_LINE_SIZE - miss_alignment); + } + } + + char* aligned_stack::next_alignment(char* pointer) { + return reinterpret_cast(next_alignment(reinterpret_cast(pointer))); + } + } + } +} diff --git a/test/base_tests.cpp b/test/base_tests.cpp index d64614a..8780513 100644 --- a/test/base_tests.cpp +++ b/test/base_tests.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include @@ -82,3 +84,66 @@ TEST_CASE( "spinlock protects concurrent counter", "[internal/base/spinlock.h]") REQUIRE(base_tests_shared_counter == 0); } } + +TEST_CASE( "aligned stack stores objects correctly", "[internal/base/aligned_stack.h]") { + constexpr long data_size = 1024; + char data[data_size]; + aligned_stack stack{data, data_size}; + + SECTION( "stack correctly pushes sub linesize objects" ) { + std::array small_data_one{'a', 'b', 'c', 'd', 'e'}; + std::array small_data_two{}; + std::array small_data_three{'A'}; + + auto pointer_one = stack.push(small_data_one); + auto pointer_two = stack.push(small_data_two); + auto pointer_three = stack.push(small_data_three); + + REQUIRE(reinterpret_cast(pointer_one) % CACHE_LINE_SIZE == 0); + REQUIRE(reinterpret_cast(pointer_two) % CACHE_LINE_SIZE == 0); + REQUIRE(reinterpret_cast(pointer_three) % CACHE_LINE_SIZE == 0); + } + + SECTION( "stack correctly pushes above linesize objects" ) { + std::array small_data_one{'a', 'b', 'c', 'd', 'e'}; + std::array big_data_one{}; + + auto big_pointer_one = stack.push(big_data_one); + auto small_pointer_one = stack.push(small_data_one); + + REQUIRE(reinterpret_cast(big_pointer_one) % CACHE_LINE_SIZE == 0); + REQUIRE(reinterpret_cast(small_pointer_one) % CACHE_LINE_SIZE == 0); + } + + SECTION( "stack correctly stores and retrieves objects" ) { + std::array data_one{'a', 'b', 'c', 'd', 'e'}; + + stack.push(data_one); + auto retrieved_data = stack.pop>(); + + REQUIRE(retrieved_data == std::array{'a', 'b', 'c', 'd', 'e'}); + } + + SECTION( "stack can push and pop multiple times with correct alignment" ) { + std::array small_data_one{'a', 'b', 'c', 'd', 'e'}; + std::array small_data_two{}; + std::array small_data_three{'A'}; + + auto pointer_one = stack.push(small_data_one); + auto pointer_two = stack.push(small_data_two); + auto pointer_three = stack.push(small_data_three); + stack.pop(); + stack.pop(); + auto pointer_four = stack.push(small_data_two); + auto pointer_five = stack.push(small_data_three); + + REQUIRE(reinterpret_cast(pointer_one) % CACHE_LINE_SIZE == 0); + REQUIRE(reinterpret_cast(pointer_two) % CACHE_LINE_SIZE == 0); + REQUIRE(reinterpret_cast(pointer_three) % CACHE_LINE_SIZE == 0); + REQUIRE(reinterpret_cast(pointer_four) % CACHE_LINE_SIZE == 0); + REQUIRE(reinterpret_cast(pointer_five) % CACHE_LINE_SIZE == 0); + + REQUIRE(pointer_four == pointer_two); + REQUIRE(pointer_five == pointer_three); + } +}