+
+/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation
+/// does not guarantee the availability of aligned_alloc(). Memory allocated with
+/// std_aligned_alloc() must be freed with std_aligned_free().
+
+void* std_aligned_alloc(size_t alignment, size_t size) {
+
+#if defined(POSIXALIGNEDALLOC)
+ void *mem;
+ return posix_memalign(&mem, alignment, size) ? nullptr : mem;
+#elif defined(_WIN32)
+ return _mm_malloc(size, alignment);
+#else
+ return std::aligned_alloc(alignment, size);
+#endif
+}
+
+void std_aligned_free(void* ptr) {
+
+#if defined(POSIXALIGNEDALLOC)
+ free(ptr);
+#elif defined(_WIN32)
+ _mm_free(ptr);
+#else
+ free(ptr);
+#endif
+}
+
+/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
+
+#if defined(_WIN32)
+
+static void* aligned_large_pages_alloc_win(size_t allocSize) {
+
+ HANDLE hProcessToken { };
+ LUID luid { };
+ void* mem = nullptr;
+
+ const size_t largePageSize = GetLargePageMinimum();
+ if (!largePageSize)
+ return nullptr;
+
+ // We need SeLockMemoryPrivilege, so try to enable it for the process
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
+ return nullptr;
+
+ if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
+ {
+ TOKEN_PRIVILEGES tp { };
+ TOKEN_PRIVILEGES prevTp { };
+ DWORD prevTpLen = 0;
+
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Luid = luid;
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds,
+ // we still need to query GetLastError() to ensure that the privileges were actually obtained.
+ if (AdjustTokenPrivileges(
+ hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) &&
+ GetLastError() == ERROR_SUCCESS)
+ {
+ // Round up size to full pages and allocate
+ allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
+ mem = VirtualAlloc(
+ NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
+
+ // Privilege no longer needed, restore previous state
+ AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL);
+ }
+ }
+
+ CloseHandle(hProcessToken);
+
+ return mem;
+}
+
+void* aligned_large_pages_alloc(size_t allocSize) {
+
+ static bool firstCall = true;
+ void* mem;
+
+ // Try to allocate large pages
+ mem = aligned_large_pages_alloc_win(allocSize);
+
+ // Suppress info strings on the first call. The first call occurs before 'uci'
+ // is received and in that case this output confuses some GUIs.
+ if (!firstCall)
+ {
+ if (mem)
+ sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl;
+ else
+ sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl;
+ }
+ firstCall = false;
+
+ // Fall back to regular, page aligned, allocation if necessary
+ if (!mem)
+ mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+
+ return mem;
+}
+
+#else
+
+void* aligned_large_pages_alloc(size_t allocSize) {
+
+#if defined(__linux__)
+ constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size
+#else
+ constexpr size_t alignment = 4096; // assumed small page size
+#endif
+
+ // round up to multiples of alignment
+ size_t size = ((allocSize + alignment - 1) / alignment) * alignment;
+ void *mem = std_aligned_alloc(alignment, size);
+#if defined(MADV_HUGEPAGE)
+ madvise(mem, size, MADV_HUGEPAGE);
+#endif
+ return mem;
+}
+
+#endif
+
+
+/// aligned_large_pages_free() will free the previously allocated ttmem
+
+#if defined(_WIN32)
+
+void aligned_large_pages_free(void* mem) {
+
+ if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
+ {
+ DWORD err = GetLastError();
+ std::cerr << "Failed to free transposition table. Error code: 0x" <<
+ std::hex << err << std::dec << std::endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+#else
+
+void aligned_large_pages_free(void *mem) {
+ std_aligned_free(mem);
+}
+
+#endif
+
+