Add 1GB page support on x86 Linux

1GB pages for the TT give a small performance bump (`./stockfish speedtest 256 32000`):

master:
Nodes/second               : 230514479
patch
Nodes/second               : 237997728

With a large enough TT there is still dTLB thrashing with 2MB pages. 1GB pages reduces that and makes page table walks a bit faster (they're shorter, and there's fewer PTEs, which fits better in cache). `madvise` won't give you huge pages, only large pages, so we use mmap here.

We guard the huge page attempt to x86 + at least 8 huge pages per NUMA node, to avoid memory oversubscription when for example running with a 1.1 GB hash. Additionally, we change the TT clearing slightly so that the pages are better distributed across NUMA nodes.

Data from Torom:

master:
Nodes/second               : 43524292
patch:
Nodes/second               : 44080034

Data on an 8-thread Emerald Rapids VM:

./stockfish speedtest 8 65536

Baseline speedtest (2MB huge pages):

Nodes/second : 7366232
Nodes/second : 7262794
Nodes/second : 7355431
Nodes/second : 7318777

Baseline speedtest (1GB huge pages):

Nodes/second : 7541027
Nodes/second : 7572720
Nodes/second : 7550268
Nodes/second : 7538925

closes https://github.com/official-stockfish/Stockfish/pull/6681

No functional change
This commit is contained in:
anematode
2026-05-24 14:17:54 +02:00
committed by Joost VandeVondele
parent 6a477d5610
commit be9df38f27
5 changed files with 103 additions and 9 deletions
+12
View File
@@ -27,6 +27,7 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "bitboard.h"
@@ -427,6 +428,10 @@ void ThreadPool::wait_for_search_finished() const {
th->wait_for_search_finished();
}
std::vector<size_t> ThreadPool::get_bound_thread_to_numa_node() const {
return boundThreadToNumaNode;
}
std::vector<size_t> ThreadPool::get_bound_thread_count_by_numa_node() const {
std::vector<size_t> counts;
@@ -446,6 +451,13 @@ std::vector<size_t> ThreadPool::get_bound_thread_count_by_numa_node() const {
return counts;
}
size_t ThreadPool::numa_nodes() const {
std::unordered_set<size_t> seen;
for (NumaIndex n : boundThreadToNumaNode)
seen.insert(n);
return std::max(seen.size(), size_t(1));
}
void ThreadPool::ensure_network_replicated() {
for (auto&& th : threads)
th->ensure_network_replicated();