upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBitflash <admin@bitflash.network>2026-08-20 18:03:59 -0300
committerBitflash <admin@bitflash.network>2026-08-20 18:03:59 -0300
commitf4d311a2b1be7ce1eb7082109a391099564271b9 (patch)
treea5f7566a29fbb20d14a68144ec8a2ed0ec4e7a30 /src
parent721a7e46fadba3251564e2308b7b7eefcb2e9d36 (diff)
parentbfce24bc542927647ae4e5af68b71e3e56e1a291 (diff)
Merge pull request 'randomx: optional 2 MB large pages for ~10% more hash rate' (#69) from randomx-large-pages into main
Diffstat (limited to 'src')
-rw-r--r--src/main_gui.cpp7
-rw-r--r--src/net.cpp1
-rw-r--r--src/randomx_pow.cpp117
-rw-r--r--src/randomx_pow.h7
4 files changed, 121 insertions, 11 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 0a961bf..a01bdee 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -127,6 +127,8 @@ static void PrintUsage()
127 printf(" /stratumbridgeport=N (default 3333; listen on 127.0.0.1)\n"); 127 printf(" /stratumbridgeport=N (default 3333; listen on 127.0.0.1)\n");
128 printf(" /solomine\n"); 128 printf(" /solomine\n");
129 printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n"); 129 printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n");
130 printf(" /nolargepages (do not ask for 2 MB pages for the RandomX\n");
131 printf(" cache, dataset and scratchpads)\n");
130 printf(" /checkblocks=N (blocks re-verified at startup, default 288, 0 = all)\n"); 132 printf(" /checkblocks=N (blocks re-verified at startup, default 288, 0 = all)\n");
131 printf("\n"); 133 printf("\n");
132 printf("Pool operator announcement:\n"); 134 printf("Pool operator announcement:\n");
@@ -230,6 +232,11 @@ static void ParseStartupArguments(int argc, char* argv[])
230 if (!strCheckBlocks.empty()) 232 if (!strCheckBlocks.empty())
231 nCheckBlocksOnLoad = atoi(strCheckBlocks.c_str()); 233 nCheckBlocksOnLoad = atoi(strCheckBlocks.c_str());
232 234
235 // Must be read before LoadBlockIndex(), which is what first calls
236 // RandomXInit() and fixes how the cache is allocated.
237 if (arg(argc,argv,"/nolargepages") || arg(argc,argv,"-nolargepages"))
238 fRandomXLargePages = false;
239
233 // /genproclimit=N -- threads to hash with. 0 or absent means automatic, 240 // /genproclimit=N -- threads to hash with. 0 or absent means automatic,
234 // which is every core but one. Named after Bitcoin's own option so it reads 241 // which is every core but one. Named after Bitcoin's own option so it reads
235 // familiarly to anyone who has run one of these before. 242 // familiarly to anyone who has run one of these before.
diff --git a/src/net.cpp b/src/net.cpp
index ecfd565..287a4bd 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -366,6 +366,7 @@ string GetDiagnosticsText()
366 nMining, 366 nMining,
367 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() 367 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str()
368 : ", not mining"); 368 : ", not mining");
369 str += strprintf(" large pages %s\n", RandomXLargePagesStatus());
369 370
370 str += SockAccountingText(); 371 str += SockAccountingText();
371 372
diff --git a/src/randomx_pow.cpp b/src/randomx_pow.cpp
index 1d058d4..4454fd1 100644
--- a/src/randomx_pow.cpp
+++ b/src/randomx_pow.cpp
@@ -23,6 +23,50 @@ static std::mutex g_csInit;
23static bool g_fInit = false; 23static bool g_fInit = false;
24static bool g_fFast = false; 24static bool g_fFast = false;
25 25
26// RandomX reads a 256 MB cache, a 2 GB dataset and a 2 MB scratchpad per VM
27// at random, so most reads miss the TLB on 4 KB pages. 2 MB pages recover
28// roughly a tenth of the hash rate.
29//
30// The pool must be reserved first (vm.nr_hugepages), and RandomX returns NULL
31// instead of falling back, so each allocation below retries without the flag.
32bool fRandomXLargePages = true; // cleared by -nolargepages
33static bool g_fLargeCache = false;
34static bool g_fLargeDataset = false;
35static bool g_fLargeAny = false;
36
37// How to reserve the large-page pool, for the hint printed when a large-page
38// allocation falls back to normal pages. The mechanism is OS-specific.
39static const char* LargePagesHint()
40{
41#ifdef _WIN32
42 return "enable the \"Lock pages in memory\" privilege and run elevated";
43#else
44 return "reserve them with: sysctl vm.nr_hugepages=1280";
45#endif
46}
47
48static randomx_flags WithLargePages(randomx_flags f)
49{
50 return (randomx_flags)(f | RANDOMX_FLAG_LARGE_PAGES);
51}
52
53// Ask for a cache in large pages, then plain pages. Sets fLargeOut to say
54// which one answered.
55static randomx_cache* AllocCache(randomx_flags flags, bool& fLargeOut)
56{
57 fLargeOut = false;
58 if (fRandomXLargePages)
59 {
60 randomx_cache* c = randomx_alloc_cache(WithLargePages(flags));
61 if (c)
62 {
63 fLargeOut = true;
64 return c;
65 }
66 }
67 return randomx_alloc_cache(flags);
68}
69
26 70
27bool RandomXInit() 71bool RandomXInit()
28{ 72{
@@ -36,28 +80,56 @@ bool RandomXInit()
36 // Detect the best flags for this CPU (JIT, hardware AES, Argon2). 80 // Detect the best flags for this CPU (JIT, hardware AES, Argon2).
37 g_flags = randomx_get_flags(); 81 g_flags = randomx_get_flags();
38 82
39 g_cache = randomx_alloc_cache(g_flags); 83 g_cache = AllocCache(g_flags, g_fLargeCache);
40 if (!g_cache) 84 if (!g_cache)
41 { 85 {
42 // Try without JIT as a fallback 86 // Try without JIT as a fallback
43 g_flags = RANDOMX_FLAG_DEFAULT; 87 g_flags = RANDOMX_FLAG_DEFAULT;
44 g_cache = randomx_alloc_cache(g_flags); 88 g_cache = AllocCache(g_flags, g_fLargeCache);
45 if (!g_cache) 89 if (!g_cache)
46 return error("RandomX: failed to allocate cache (256 MB)"); 90 return error("RandomX: failed to allocate cache (256 MB)");
47 } 91 }
48 randomx_init_cache(g_cache, RANDOMX_KEY, strlen(RANDOMX_KEY)); 92 randomx_init_cache(g_cache, RANDOMX_KEY, strlen(RANDOMX_KEY));
49 93
50 // Verification VM in light mode (cache only) 94 // Verification VM in light mode (cache only). The scratchpad is only 2 MB
51 g_vmVerify = randomx_create_vm(g_flags, g_cache, NULL); 95 // and this VM is created once, but it hashes every block the node ever
96 // validates, so it gets the same treatment.
97 bool fLargeVerify = false;
98 if (fRandomXLargePages)
99 {
100 g_vmVerify = randomx_create_vm(WithLargePages(g_flags), g_cache, NULL);
101 fLargeVerify = (g_vmVerify != NULL);
102 }
103 if (!g_vmVerify)
104 g_vmVerify = randomx_create_vm(g_flags, g_cache, NULL);
52 if (!g_vmVerify) 105 if (!g_vmVerify)
53 return error("RandomX: failed to create verification VM"); 106 return error("RandomX: failed to create verification VM");
54 107
108 g_fLargeAny = g_fLargeCache || fLargeVerify;
55 g_fInit = true; 109 g_fInit = true;
56 printf("RandomX: initialized (cache 256 MB, flags=%d)\n", (int)g_flags); 110 printf("RandomX: initialized (cache 256 MB, flags=%d, large pages: %s)\n",
111 (int)g_flags, RandomXLargePagesStatus());
112 if (fRandomXLargePages && !g_fLargeCache)
113 printf("RandomX: large pages unavailable for the cache; %s "
114 "for about 10%% more hash rate\n", LargePagesHint());
57 return true; 115 return true;
58} 116}
59 117
60 118
119const char* RandomXLargePagesStatus()
120{
121 if (!fRandomXLargePages)
122 return "off (-nolargepages)";
123 if (g_fLargeCache && g_fLargeDataset)
124 return "cache + dataset";
125 if (g_fLargeCache)
126 return "cache only";
127 if (g_fLargeDataset)
128 return "dataset only";
129 return g_fLargeAny ? "scratchpad only" : "unavailable";
130}
131
132
61// One dataset, once, no matter how many miners ask for it. 133// One dataset, once, no matter how many miners ask for it.
62// 134//
63// Every miner thread calls this at startup. The g_fFast guard below is only 135// Every miner thread calls this at startup. The g_fFast guard below is only
@@ -87,8 +159,18 @@ bool RandomXInitDataset(int nThreads)
87 if (g_fFast) 159 if (g_fFast)
88 return true; 160 return true;
89 161
90 randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM); 162 // Only RANDOMX_FLAG_LARGE_PAGES means anything to randomx_alloc_dataset;
91 g_dataset = randomx_alloc_dataset(fastFlags); 163 // the rest of g_flags belongs to the VMs that read it.
164 if (fRandomXLargePages)
165 {
166 g_dataset = randomx_alloc_dataset(WithLargePages(RANDOMX_FLAG_DEFAULT));
167 g_fLargeDataset = (g_dataset != NULL);
168 if (!g_dataset)
169 printf("RandomX: large pages unavailable for the 2 GB dataset; %s "
170 "for about 10%% more hash rate\n", LargePagesHint());
171 }
172 if (!g_dataset)
173 g_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
92 if (!g_dataset) 174 if (!g_dataset)
93 { 175 {
94 printf("RandomX: not enough memory for the 2 GB dataset, staying in light mode\n"); 176 printf("RandomX: not enough memory for the 2 GB dataset, staying in light mode\n");
@@ -142,13 +224,26 @@ void* RandomXCreateMinerVM()
142{ 224{
143 if (!g_fInit && !RandomXInit()) 225 if (!g_fInit && !RandomXInit())
144 return NULL; 226 return NULL;
227
228 randomx_flags flags = g_flags;
229 randomx_cache* cache = g_cache;
230 randomx_dataset* dataset = NULL;
145 if (g_fFast && g_dataset) 231 if (g_fFast && g_dataset)
146 { 232 {
147 randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM); 233 flags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM);
148 return randomx_create_vm(fastFlags, NULL, g_dataset); 234 cache = NULL;
235 dataset = g_dataset;
236 }
237
238 // Each thread gets its own 2 MB scratchpad, so the large-page pool can run
239 // dry partway through a fleet of miners. Threads that miss out still run.
240 if (fRandomXLargePages)
241 {
242 randomx_vm* vm = randomx_create_vm(WithLargePages(flags), cache, dataset);
243 if (vm)
244 return vm;
149 } 245 }
150 // Light mode: own VM sharing the cache 246 return randomx_create_vm(flags, cache, dataset);
151 return randomx_create_vm(g_flags, g_cache, NULL);
152} 247}
153 248
154 249
diff --git a/src/randomx_pow.h b/src/randomx_pow.h
index 0027571..355b708 100644
--- a/src/randomx_pow.h
+++ b/src/randomx_pow.h
@@ -41,4 +41,11 @@ uint256 RandomXHashWithVM(void* vm, const void* pHeader, size_t nSize);
41// True when the 2 GB dataset is ready (fast mode active). 41// True when the 2 GB dataset is ready (fast mode active).
42bool RandomXFastReady(); 42bool RandomXFastReady();
43 43
44// Ask for large (2 MB) pages for the cache, dataset and scratchpads. Set false
45// by -nolargepages. Must be set before the first RandomXInit().
46extern bool fRandomXLargePages;
47
48// Which allocations actually got large pages, for the status display.
49const char* RandomXLargePagesStatus();
50
44#endif 51#endif