upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/main_gui.cpp
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-07-30 14:07:50 -0300
committerGitHub <noreply@github.com>2026-07-30 14:07:50 -0300
commit225d0210f881acb10a3dfaf9bc5a23446bc12d21 (patch)
treef8f15df707084a64924bed42e526c2e0ef43b1b9 /src/main_gui.cpp
parent9f7141b28e2da9db7d7cb099d1f13f28742ff0fa (diff)
mine: hash on every core instead of one (#52)
Satoshi left a note above ThreadBitcoinMiner in 2009: //// todo: start one thread per processor, //// use getenv("NUMBER_OF_PROCESSORS") Nobody did, and it stopped being a nicety when the proof of work became RandomX. SHA-256d on a 2009 desktop lost little to a single thread. RandomX is built to be fed by every core at once, so one thread on a 32-core machine mines at a thirty-second of what the box can do -- and the only outward sign is a CPU that looks suspiciously idle. A user reported exactly that: 30 minutes of "Mining", no blocks, Bitflash sitting at 3.3% CPU while the 2 GB dataset was allocated and fast mode was active. Nothing was broken. It was hashing on one core. bitflash -gen # every core but one bitflash -gen -genproclimit=8 # exactly eight Automatic leaves one core to the rest of the node -- accepting blocks, talking to peers, drawing the window -- because a node that mines beautifully while falling behind the chain has won nothing. /genproclimit overrides it, and a count several times the core count is refused rather than quietly thrashing. The wrapper was already built for this: RandomXCreateMinerVM says "own VM, not shared", and the ~2 GB dataset is one allocation every thread reads. Only the 2 MB scratchpad multiplies, so sixteen threads cost about 32 MB more than one. The startup line says so, because "why is it using 2 GB" is the first question anyone asks. Two things had to be right for this to be safe: - Shutdown. Miners share slot 3 of vfThreadRunning and StopNode waits for every slot before DBFlush tears the database down. With several, the first one out announced that everyone had finished while the rest were still calling ProcessBlock, so they are counted now and only the last one clears the flag. The three CheckForShutdown(3) calls inside the miner became plain returns: _endthread() does not unwind on Windows, so the bookkeeping would have been skipped there. - Duplicated work. Each thread makes its own key, and the coinbase pays it, so every thread has a different merkle root and searches a different space. Block assembly was already under cs_main and cs_mapTransactions; AddKey and ProcessBlock take their own locks. Verified on both platforms: four distinct thread ids reach the hashing loop and all report back on shutdown, which exits 0 with DBFlush running. Auto picks cores-1 (1 on a 2-core box, correctly reported). Speedup itself needs a many-core machine to show and has not been measured here -- the claim in this message is about threads doing work, not a benchmark. Not touched: RandomX's memory. RANDOMX_DATASET_BASE_SIZE and SCRATCHPAD_L3 are part of the hash function, so a knob for "use more RAM" would either do nothing or fork the chain. Threads are the only real dial.
Diffstat (limited to 'src/main_gui.cpp')
-rw-r--r--src/main_gui.cpp25
1 files changed, 23 insertions, 2 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 2484592..852ad4d 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -1,6 +1,7 @@
1// Bitflash entry point -- starts node threads then runs GUI (or headless). 1// Bitflash entry point -- starts node threads then runs GUI (or headless).
2 2
3#include "headers_core.h" 3#include "headers_core.h"
4#include <thread> // hardware_concurrency, to sanity-check /genproclimit
4#ifndef _WIN32 5#ifndef _WIN32
5#include <csignal> 6#include <csignal>
6#endif 7#endif
@@ -52,6 +53,7 @@ static void PrintUsage()
52 printf(" /operator\n"); 53 printf(" /operator\n");
53 printf(" /participant=POOL_BTF_ADDRESS\n"); 54 printf(" /participant=POOL_BTF_ADDRESS\n");
54 printf(" /solomine\n"); 55 printf(" /solomine\n");
56 printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n");
55 printf("\n"); 57 printf("\n");
56 printf("Pool operator announcement:\n"); 58 printf("Pool operator announcement:\n");
57 printf(" /poolname=NAME\n"); 59 printf(" /poolname=NAME\n");
@@ -99,6 +101,26 @@ static void ParseStartupArguments(int argc, char* argv[])
99 if (arg(argc,argv,"/solomine") || arg(argc,argv,"-solomine")) 101 if (arg(argc,argv,"/solomine") || arg(argc,argv,"-solomine"))
100 fSoloMineTest = true; 102 fSoloMineTest = true;
101 103
104 // /genproclimit=N -- threads to hash with. 0 or absent means automatic,
105 // which is every core but one. Named after Bitcoin's own option so it reads
106 // familiarly to anyone who has run one of these before.
107 string strProcLimit = argval2(argc, argv, "/genproclimit", "-genproclimit");
108 if (!strProcLimit.empty())
109 {
110 int n = atoi(strProcLimit.c_str());
111 unsigned int nCores = std::thread::hardware_concurrency();
112 if (n < 0)
113 fprintf(stderr, "Ignoring /genproclimit=%s: not a thread count\n",
114 strProcLimit.c_str());
115 else if (nCores > 0 && n > (int)nCores * 4)
116 // Well past diminishing returns, and every thread still costs a
117 // scratchpad. Refuse rather than quietly thrash the machine.
118 fprintf(stderr, "Ignoring /genproclimit=%d: this machine has %u core(s)\n",
119 n, nCores);
120 else
121 nMinerThreads = n; // 0 stays automatic
122 }
123
102 if (arg(argc,argv,"/operator") || arg(argc,argv,"-operator")) 124 if (arg(argc,argv,"/operator") || arg(argc,argv,"-operator"))
103 nMineMode = MINE_OPERATOR; 125 nMineMode = MINE_OPERATOR;
104 126
@@ -289,8 +311,7 @@ int main(int argc, char* argv[])
289 printf("Error: _beginthread(ThreadRPCServer) failed\n"); 311 printf("Error: _beginthread(ThreadRPCServer) failed\n");
290 } 312 }
291 if (fGenerateBitcoins) 313 if (fGenerateBitcoins)
292 if (_beginthread(ThreadBitcoinMiner, 0, NULL) == (uintptr_t)-1) 314 StartMinerThreads();
293 printf("Error: _beginthread(ThreadBitcoinMiner) failed\n");
294 315
295#ifdef BITFLASH_NO_GUI 316#ifdef BITFLASH_NO_GUI
296 // Nothing else this binary can do; /nogui is accepted and redundant. 317 // Nothing else this binary can do; /nogui is accepted and redundant.