From f9e7ee6355b5627a673491b7b03f0258ad57522b Mon Sep 17 00:00:00 2001 From: TheRealGioviok <425gioviok@gmail.com> Date: Wed, 31 Dec 2025 10:52:46 +0100 Subject: [PATCH 1/5] Tentative Bench: 14055228 --- CMakeLists.txt | 1 + src/search.cpp | 5 +- src/search.hpp | 2 +- src/tt.cpp | 7 +-- src/tt.hpp | 4 +- src/util/large_pages.hpp | 110 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 src/util/large_pages.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 73636245..66d66dea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,7 @@ set(srcs src/util/bit.hpp src/util/parse.hpp src/util/pretty.hpp + src/util/large_pages.hpp src/util/static_vector.hpp src/util/types.hpp src/util/vec/sse2.hpp diff --git a/src/search.cpp b/src/search.cpp index 07f9fc98..0fd7fcbe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "util/large_pages.hpp" #include namespace Clockwork { @@ -118,9 +119,9 @@ void Searcher::initialize(size_t thread_count) { started_barrier = std::make_unique>(1 + thread_count); if (thread_count > 0) { - m_workers.push_back(std::make_unique(*this, ThreadType::MAIN)); + m_workers.push_back(make_unique_huge_page(*this, ThreadType::MAIN)); for (size_t i = 1; i < thread_count; i++) { - m_workers.push_back(std::make_unique(*this, ThreadType::SECONDARY)); + m_workers.push_back(make_unique_huge_page(*this, ThreadType::SECONDARY)); } } } diff --git a/src/search.hpp b/src/search.hpp index 427bbb84..8f1764fc 100644 --- a/src/search.hpp +++ b/src/search.hpp @@ -126,7 +126,7 @@ class Searcher { } private: - std::vector> m_workers; + std::vector> m_workers; }; class alignas(128) Worker { diff --git a/src/tt.cpp b/src/tt.cpp index 8f3c7456..52368c91 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -63,10 +63,6 @@ TT::TT(size_t mb) : resize(mb); } -TT::~TT() { - aligned_free(m_clusters); -} - std::optional TT::probe(const Position& pos, i32 ply) const { size_t idx = mulhi64(pos.get_hash_key(), m_size); const auto cluster = this->m_clusters[idx].load(); @@ -161,13 +157,12 @@ void TT::store(const Position& pos, } void TT::resize(size_t mb) { - aligned_free(m_clusters); size_t bytes = mb * 1024 * 1024; size_t entries = bytes / sizeof(TTClusterMemory); m_size = entries; - m_clusters = static_cast(aligned_alloc(TT_ALIGNMENT, bytes)); + m_clusters = make_unique_for_overwrite_huge_page(m_size); clear(); } diff --git a/src/tt.hpp b/src/tt.hpp index 1c802f67..5129ad35 100644 --- a/src/tt.hpp +++ b/src/tt.hpp @@ -3,6 +3,7 @@ #include "position.hpp" #include #include +#include "util/large_pages.hpp" #include namespace Clockwork { @@ -90,7 +91,6 @@ class TT { static constexpr u8 AGE_MASK = 0x1F; TT(size_t mb = DEFAULT_SIZE_MB); - ~TT(); std::optional probe(const Position& position, i32 ply) const; void store(const Position& position, @@ -107,7 +107,7 @@ class TT { i32 hashfull() const; private: - TTClusterMemory* m_clusters; + unique_ptr_huge_page m_clusters; size_t m_size; u8 m_age; }; diff --git a/src/util/large_pages.hpp b/src/util/large_pages.hpp new file mode 100644 index 00000000..8ce8afee --- /dev/null +++ b/src/util/large_pages.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ + #include +#elif defined(_WIN32) + #include +#endif + +template +using unique_ptr_huge_page = + std::conditional_t, + std::unique_ptr*)>>, + std::unique_ptr>>; + +template +T* allocate_huge_page(std::size_t size) { + constexpr static auto huge_page_size = 2 * 1024 * 1024; // 2MB pages + size = ((size + huge_page_size - 1) / huge_page_size) * huge_page_size; + +#ifdef __linux__ + T* data = static_cast(std::aligned_alloc(huge_page_size, size)); + if (data) { + madvise(data, size, MADV_HUGEPAGE); + } + return data; +#elif defined(_WIN32) + // On Windows, use standard allocation with 2MB alignment hint + // VirtualAlloc with MEM_LARGE_PAGES requires privileges and uses larger pages (typically 2MB+) + // For consistent 2MB behavior, we use standard allocation + T* data = + static_cast(VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + return data; +#else + // Fallback for other platforms + T* data = static_cast(std::aligned_alloc(huge_page_size, size)); + return data; +#endif +} + +template +void deallocate_huge_page(T* ptr) { +#ifdef __linux__ + std::free(ptr); +#elif defined(_WIN32) + VirtualFree(ptr, 0, MEM_RELEASE); +#else + std::free(ptr); +#endif +} + +template + requires(!std::is_array_v) +unique_ptr_huge_page make_unique_huge_page(Args&&... args) { + T* data = allocate_huge_page(sizeof(T)); + std::construct_at(data, std::forward(args)...); + return unique_ptr_huge_page(data, [](T* ptr) { + std::destroy_at(ptr); + deallocate_huge_page(ptr); + }); +} + +template + requires std::is_unbounded_array_v +unique_ptr_huge_page make_unique_huge_page(std::size_t n) { + using E = std::remove_all_extents_t; + E* data = allocate_huge_page(n * sizeof(E)); + std::uninitialized_value_construct_n(data, n); + return unique_ptr_huge_page(data, [n](E* ptr) { + std::destroy_n(ptr, n); + deallocate_huge_page(ptr); + }); +} + +template + requires std::is_bounded_array_v +void make_unique_huge_page(Args&&...) = delete; + +template + requires(!std::is_array_v) +unique_ptr_huge_page make_unique_for_overwrite_huge_page() { + T* data = allocate_huge_page(sizeof(T)); + new (data) T; + return unique_ptr_huge_page(data, [](T* ptr) { + std::destroy_at(ptr); + deallocate_huge_page(ptr); + }); +} + +template + requires std::is_unbounded_array_v +unique_ptr_huge_page make_unique_for_overwrite_huge_page(std::size_t n) { + using E = std::remove_all_extents_t; + E* data = allocate_huge_page(n * sizeof(E)); + std::uninitialized_default_construct_n(data, n); + return unique_ptr_huge_page(data, [n](E* ptr) { + std::destroy_n(ptr, n); + deallocate_huge_page(ptr); + }); +} + +template + requires std::is_bounded_array_v +void make_unique_for_overwrite_huge_page(Args&&...) = delete; From 3de32896b2e6501e9cdce083911b87a87ad246c1 Mon Sep 17 00:00:00 2001 From: TheRealGioviok <425gioviok@gmail.com> Date: Wed, 31 Dec 2025 10:56:20 +0100 Subject: [PATCH 2/5] Bench: 14055228 --- src/util/large_pages.hpp | 58 +++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/src/util/large_pages.hpp b/src/util/large_pages.hpp index 8ce8afee..75d426e2 100644 --- a/src/util/large_pages.hpp +++ b/src/util/large_pages.hpp @@ -22,23 +22,69 @@ using unique_ptr_huge_page = template T* allocate_huge_page(std::size_t size) { constexpr static auto huge_page_size = 2 * 1024 * 1024; // 2MB pages - size = ((size + huge_page_size - 1) / huge_page_size) * huge_page_size; #ifdef __linux__ + size = ((size + huge_page_size - 1) / huge_page_size) * huge_page_size; T* data = static_cast(std::aligned_alloc(huge_page_size, size)); if (data) { madvise(data, size, MADV_HUGEPAGE); } return data; #elif defined(_WIN32) - // On Windows, use standard allocation with 2MB alignment hint - // VirtualAlloc with MEM_LARGE_PAGES requires privileges and uses larger pages (typically 2MB+) - // For consistent 2MB behavior, we use standard allocation - T* data = - static_cast(VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + HANDLE hToken; + TOKEN_PRIVILEGES tp; + LUID luid; + + // Get the current process token + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { + return static_cast( + VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + } + + // Get the LUID for the SeLockMemoryPrivilege + if (!LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid)) { + CloseHandle(hToken); + return static_cast( + VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + } + + // Enable the SeLockMemoryPrivilege + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), nullptr, nullptr)) { + CloseHandle(hToken); + return static_cast( + VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + } + + // Even if AdjustTokenPrivileges returns success, must check GetLastError for ERROR_NOT_ALL_ASSIGNED + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { + CloseHandle(hToken); + return static_cast( + VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + } + + // Get the large page minimum size (typically 2MB on x64 Windows) + SIZE_T largePageMinimum = GetLargePageMinimum(); + SIZE_T roundedSize = ((size + largePageMinimum - 1) / largePageMinimum) * largePageMinimum; + + // Allocate with MEM_LARGE_PAGES + T* data = static_cast(VirtualAlloc( + nullptr, roundedSize, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE)); + + if (!data) { + CloseHandle(hToken); + return static_cast( + VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + } + + CloseHandle(hToken); return data; #else // Fallback for other platforms + size = ((size + huge_page_size - 1) / huge_page_size) * huge_page_size; T* data = static_cast(std::aligned_alloc(huge_page_size, size)); return data; #endif From dad51d078559869faf2e11c701f2af7e9be6cb5f Mon Sep 17 00:00:00 2001 From: TheRealGioviok <425gioviok@gmail.com> Date: Sat, 3 Jan 2026 12:50:24 +0100 Subject: [PATCH 3/5] Bench 13398527 From 9b218e6180294cbfe5397e66bebc8b86bdacce64 Mon Sep 17 00:00:00 2001 From: TheRealGioviok <425gioviok@gmail.com> Date: Sat, 3 Jan 2026 12:50:35 +0100 Subject: [PATCH 4/5] Bench: 13398527 From 719d1fa317d590832ca1f29964192c20728f376b Mon Sep 17 00:00:00 2001 From: TheRealGioviok <425gioviok@gmail.com> Date: Sat, 3 Jan 2026 15:02:42 +0100 Subject: [PATCH 5/5] Bench: 13398527 --- src/search.cpp | 2 +- src/tt.hpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0fd7fcbe..6269d25c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -10,6 +10,7 @@ #include "tm.hpp" #include "tuned.hpp" #include "uci.hpp" +#include "util/large_pages.hpp" #include "util/log2.hpp" #include "util/types.hpp" #include @@ -19,7 +20,6 @@ #include #include #include -#include "util/large_pages.hpp" #include namespace Clockwork { diff --git a/src/tt.hpp b/src/tt.hpp index 5129ad35..5984c59b 100644 --- a/src/tt.hpp +++ b/src/tt.hpp @@ -1,9 +1,9 @@ #pragma once #include "position.hpp" +#include "util/large_pages.hpp" #include #include -#include "util/large_pages.hpp" #include namespace Clockwork { @@ -108,8 +108,8 @@ class TT { private: unique_ptr_huge_page m_clusters; - size_t m_size; - u8 m_age; + size_t m_size; + u8 m_age; }; } // namespace Clockwork