From 48948bd79f75576437f1ae6ddf6f4d597ada67f5 Mon Sep 17 00:00:00 2001 From: deegan Date: Tue, 8 Oct 2024 14:56:00 +0200 Subject: [PATCH] huge paging --- inc/mkn/kul/alloc.hpp | 86 ++++++++++++++++++++++++++++++-- inc/mkn/kul/vector.hpp | 30 +++++++---- mkn.yaml | 13 +++-- test/test/no_construct_alloc.cpp | 67 +++++++++++++++++++------ 4 files changed, 164 insertions(+), 32 deletions(-) diff --git a/inc/mkn/kul/alloc.hpp b/inc/mkn/kul/alloc.hpp index f62803f..239f7ca 100644 --- a/inc/mkn/kul/alloc.hpp +++ b/inc/mkn/kul/alloc.hpp @@ -37,6 +37,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include +#include // posix_memalign +#include // madvise + namespace mkn::kul { template @@ -98,10 +102,7 @@ class Allocator { using other = Allocator; }; - T* allocate(std::size_t const n) const { - if (n == 0) return nullptr; - return static_cast(::operator new(n * sizeof(T))); - } + T* allocate(std::size_t const n) const { return static_cast(::operator new(n * sizeof(T))); } void deallocate(T* const p) noexcept { if (p) ::operator delete(p); @@ -117,16 +118,93 @@ class Allocator { template class NonConstructingAllocator : public Allocator { + using This = NonConstructingAllocator; + public: template struct rebind { using other = NonConstructingAllocator; }; + T* allocate(std::size_t const n) const { + // if (n == 0) return nullptr; + return static_cast(malloc(n * sizeof(T))); + } + + template + void construct(U* /*ptr*/, Args&&... /*args*/) {} // nothing + template + void construct(U* /*ptr*/) noexcept(std::is_nothrow_default_constructible::value) {} + + bool operator!=(This const& that) const { return !(*this == that); } + bool operator==(This const& /*that*/) const { + return true; // stateless + } +}; + +template +class HugePageAllocator : public Allocator { + using This = HugePageAllocator; + + public: + template + struct rebind { + using other = HugePageAllocator; + }; + + HugePageAllocator() = default; + template + constexpr HugePageAllocator(HugePageAllocator const&) noexcept {} + + T* allocate(std::size_t n) { + if (n > std::numeric_limits::max() / sizeof(T)) throw std::bad_alloc(); + void* p = nullptr; + posix_memalign(&p, huge_page_size, n * sizeof(T)); + madvise(p, n * sizeof(T), MADV_HUGEPAGE); + if (p == nullptr) throw std::bad_alloc(); + return static_cast(p); + } + void deallocate(T* p, std::size_t n) { std::free(p); } + bool operator!=(This const& that) const { return !(*this == that); } + bool operator==(This const& /*that*/) const { + return true; // stateless + } +}; + +template +class NonConstructingHugePageAllocator : public NonConstructingAllocator { + using This = NonConstructingHugePageAllocator; + + public: + template + struct rebind { + using other = NonConstructingHugePageAllocator; + }; + + NonConstructingHugePageAllocator() = default; + template + constexpr NonConstructingHugePageAllocator(NonConstructingHugePageAllocator const&) noexcept {} + + T* allocate(std::size_t n) { + if (n > std::numeric_limits::max() / sizeof(T)) throw std::bad_alloc(); + void* p = nullptr; + posix_memalign(&p, huge_page_size, n * sizeof(T)); + madvise(p, n * sizeof(T), MADV_HUGEPAGE); + if (p == nullptr) throw std::bad_alloc(); + return static_cast(p); + } + + void deallocate(T* p, std::size_t n) { std::free(p); } + template void construct(U* /*ptr*/, Args&&... /*args*/) {} // nothing template void construct(U* /*ptr*/) noexcept(std::is_nothrow_default_constructible::value) {} + + bool operator!=(This const& that) const { return !(*this == that); } + bool operator==(This const& /*that*/) const { + return true; // stateless + } }; } // namespace mkn::kul diff --git a/inc/mkn/kul/vector.hpp b/inc/mkn/kul/vector.hpp index 824927e..c1f81c3 100644 --- a/inc/mkn/kul/vector.hpp +++ b/inc/mkn/kul/vector.hpp @@ -31,15 +31,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef _MKN_KUL_VECTOR_HPP_ #define _MKN_KUL_VECTOR_HPP_ +#include "alloc.hpp" #include "mkn/kul/alloc.hpp" #include +#include namespace mkn::kul { -template // A ignored but there for std::vector interop +template // A ignored but there for std::vector interop +using Vector = std::vector>; + +template // A ignored but there for std::vector interop using NonConstructingVector = std::vector>; +template +using NonConstructingHugePageVector = std::vector>; + +template +using HugePageVector = std::vector>; + template std::vector>& as_super(std::vector>& v) { return *reinterpret_cast>*>(&v); @@ -51,26 +62,27 @@ std::vector> const& as_super(std::vector -bool operator==(std::vector const& v0, mkn::kul::NonConstructingVector const& v1) { +template , A1>, bool> = 0> +bool operator==(std::vector const& v0, std::vector const& v1) { if (v0.size() != v1.size()) return false; for (std::size_t i = 0; i < v0.size(); i++) if (v0[i] != v1[i]) return false; return true; } -template -bool operator==(mkn::kul::NonConstructingVector const& v0, std::vector const& v1) { +template +bool operator==(mkn::kul::Vector const& v0, std::vector const& v1) { return v1 == v0; } -template -bool operator!=(std::vector const& v0, mkn::kul::NonConstructingVector const& v1) { +template +bool operator!=(std::vector const& v0, mkn::kul::Vector const& v1) { return !(v0 == v1); } -template -bool operator!=(mkn::kul::NonConstructingVector const& v0, std::vector const& v1) { +template +bool operator!=(mkn::kul::Vector const& v0, std::vector const& v1) { return !(v0 == v1); } diff --git a/mkn.yaml b/mkn.yaml index 18904ed..56594de 100644 --- a/mkn.yaml +++ b/mkn.yaml @@ -1,4 +1,4 @@ -#! clean build -dtOp test +#! clean build -dtKOp test # mkn.kul - Kommon Usage Library # Cross platform wrapper for systems operations / IO / threads / processes @@ -28,16 +28,21 @@ profile: nix: ./src/os/nixish arg: -D_MKN_KUL_COMPILED_LIB_ - - name: test + + - name: _test parent: lib inc: . - main: test/test.cpp - src: test/test mode: none dep: google.test if_arg: win_shared: -DGTEST_LINKED_AS_SHARED_LIBRARY=1 + - name: test + parent: _test + main: test/test.cpp + src: test/test + mode: none + - name: bench parent: lib main: test/bench.cpp diff --git a/test/test/no_construct_alloc.cpp b/test/test/no_construct_alloc.cpp index 8c57bfe..62ff2e4 100644 --- a/test/test/no_construct_alloc.cpp +++ b/test/test/no_construct_alloc.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -61,6 +62,27 @@ auto copy_manual(V const& v) { return out; } +template +auto copy_mem(V const& v) { + KUL_DBG_FUNC_ENTER; + V out; + out.reserve(v.capacity()); + out.resize(v.size()); + memcpy(out.data(), v.data(), v.size() * sizeof(typename V::value_type)); + return out; +} + +template +auto copy_loop(V const& v) { + KUL_DBG_FUNC_ENTER; + V out; + out.reserve(v.capacity()); + out.resize(v.size()); + for (std::size_t i = 0; i < v.size(); ++i) out[i] = v[i]; + // memcpy(out.data(), v.data(), v.size() * sizeof(typename V::value_type)); + return out; +} + template auto make_vector(std::size_t const& size) { KUL_DBG_FUNC_ENTER; @@ -74,25 +96,40 @@ auto make_vector_from(V1 const& v1) { return v; } +template +void resize(V0& v) { + KUL_DBG_FUNC_ENTER; + v.resize(v.capacity() + 1); // force reallocation +} + template void do_compare() { constexpr static std::size_t N = 2000000; auto const std_vec = make_vector>(N); - auto const std_vec2 = make_vector_from>(std_vec); - auto const no_construct_vec = make_vector_from>(std_vec); - if (std_vec != no_construct_vec) throw std::runtime_error("FAIL"); - if (std_vec != std_vec2) throw std::runtime_error("FAIL"); auto const v0 = copy_construct(std_vec); - auto const v1 = copy_construct(no_construct_vec); - auto const v2 = copy_manual(std_vec); - auto const v3 = copy_manual(no_construct_vec); - auto const v4 = copy_operator_equal_super(no_construct_vec); - - if (v0 != std_vec) throw std::runtime_error("FAIL 0"); - if (v0 == v1) throw std::runtime_error("FAIL 1"); // :( - if (v0 != v2) throw std::runtime_error("FAIL 2"); - if (v0 != v3) throw std::runtime_error("FAIL 3"); - if (v0 != v4) throw std::runtime_error("FAIL 4"); + + auto wash = [&](auto const& name) { + KOUT(NON) << name; + auto const copy = make_vector_from(v0); + auto const v1 = copy_mem(copy); + auto const v2 = copy_loop(copy); + auto const v3 = copy_manual(copy); + + if (v0 != v1) throw std::runtime_error("FAIL 1"); // :( + if (v0 != v2) throw std::runtime_error("FAIL 2"); + if (v0 != v3) throw std::runtime_error("FAIL 3"); + }; + + using namespace mkn::kul; + wash.template operator()>("std::vector"); + wash.template operator()>("NonConstructingVector"); + wash.template operator()>("HugePageVector"); + wash.template operator()>("NonConstructingHugePageVector"); } -TEST(NoConstructAllocator, copies) { do_compare>(); } +TEST(NoConstructAllocator, copies) { do_compare(); } + +// int main(int argc, char* argv[]) { +// ::testing::InitGoogleTest(&argc, argv); +// return RUN_ALL_TESTS(); +// }