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-maintainer <noreply@bitflash.invalid>2026-08-13 17:29:10 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 22:48:27 +0000
commit88d30ade28d6836e64d0157da57896bdb320e8a2 (patch)
tree9fd87eca1ec870aeb4b09f4387df71486e67c20b /src
parent5490d6682be52a3de5388086b01fc84bea195d61 (diff)
size thread pools by the cores we are allowed, not the ones the host has
std::thread::hardware_concurrency() reports the MACHINE's core count. Inside a container that is the wrong number: with --cpuset-cpus 0-5 on a 12-core host it still answers 12, and every caller that sized a pool from it oversubscribed 2x. Measured on the pool box: - the pool node logged "Mining with 11 thread(s) of 12 core(s)" while confined to 6 CPUs - a burst miner launched with /hashthreads=2 filled the ~2 GB RandomX dataset with 12 threads, alongside the pool node in the same 8 GB container AvailableCoreCount() asks sched_getaffinity what the cpuset actually permits, which is the number all of these meant. It cannot make the dataset build slower: the CPU budget is identical either way, so fewer threads on the same cores is the same work without the thrash. Falls back to hardware_concurrency where the call is unavailable, and never returns 0 because callers subtract from it. Not the cause of the burst miner dying -- that was a corrupt Berkeley DB environment in the worker datadir -- but it is why the failure landed on the dataset build, and it is wrong on its own terms. (cherry picked from commit b3bee16f3eef8982bff1174bd9d0a9c21473732c)
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp12
-rw-r--r--src/main_gui.cpp2
-rw-r--r--src/net.cpp2
-rw-r--r--src/util.h35
4 files changed, 45 insertions, 6 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 11eb11d..62068d2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -164,7 +164,7 @@ int MinerThreadCount()
164{ 164{
165 if (nMinerThreads > 0) 165 if (nMinerThreads > 0)
166 return nMinerThreads; 166 return nMinerThreads;
167 unsigned int nCores = std::thread::hardware_concurrency(); 167 unsigned int nCores = AvailableCoreCount();
168 if (nCores <= 1) 168 if (nCores <= 1)
169 return 1; 169 return 1;
170 return (int)(nCores - 1); 170 return (int)(nCores - 1);
@@ -3827,7 +3827,11 @@ connected:
3827 // ------------------------------------------------------------------ 3827 // ------------------------------------------------------------------
3828 if (!RandomXFastReady()) { 3828 if (!RandomXFastReady()) {
3829 SetParticipantMiningStatus("building RandomX dataset (~2GB, one-time)"); 3829 SetParticipantMiningStatus("building RandomX dataset (~2GB, one-time)");
3830 unsigned int nThreads = std::thread::hardware_concurrency(); 3830 // Fill with the cores we are permitted to use, not the ones the host
3831 // happens to have. More threads than the cpuset allows does not build
3832 // the dataset any faster -- the CPU budget is the same either way --
3833 // it just oversubscribes, and this runs alongside the pool node.
3834 unsigned int nThreads = AvailableCoreCount();
3831 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1); 3835 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1);
3832 } 3836 }
3833 LogPrint("worker", "[worker] RandomX dataset ready (%s)\n", 3837 LogPrint("worker", "[worker] RandomX dataset ready (%s)\n",
@@ -3866,7 +3870,7 @@ connected:
3866 // publish jobs to the threads, submit the shares they find. 3870 // publish jobs to the threads, submit the shares they find.
3867 // ------------------------------------------------------------------ 3871 // ------------------------------------------------------------------
3868 int nWorkers = nHashThreads > 0 ? nHashThreads 3872 int nWorkers = nHashThreads > 0 ? nHashThreads
3869 : (int)std::thread::hardware_concurrency(); 3873 : (int)AvailableCoreCount();
3870 if (nHashThreads <= 0) { 3874 if (nHashThreads <= 0) {
3871 if (nWorkers < 2) nWorkers = 2; 3875 if (nWorkers < 2) nWorkers = 2;
3872 nWorkers -= 1; // leave one core for node + I/O threads 3876 nWorkers -= 1; // leave one core for node + I/O threads
@@ -4177,7 +4181,7 @@ bool BitcoinMiner(int nThreadId)
4177 // Build the fast (~2 GB dataset) RandomX mode once, up front, so we're 4181 // Build the fast (~2 GB dataset) RandomX mode once, up front, so we're
4178 // not stuck mining in the much slower light/cache-only mode forever. 4182 // not stuck mining in the much slower light/cache-only mode forever.
4179 if (!RandomXFastReady()) { 4183 if (!RandomXFastReady()) {
4180 unsigned int nThreads = std::thread::hardware_concurrency(); 4184 unsigned int nThreads = AvailableCoreCount();
4181 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1); 4185 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1);
4182 } 4186 }
4183 4187
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index c20ae46..bee4b95 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -202,7 +202,7 @@ static void ParseStartupArguments(int argc, char* argv[])
202 if (!strProcLimit.empty()) 202 if (!strProcLimit.empty())
203 { 203 {
204 int n = atoi(strProcLimit.c_str()); 204 int n = atoi(strProcLimit.c_str());
205 unsigned int nCores = std::thread::hardware_concurrency(); 205 unsigned int nCores = AvailableCoreCount();
206 if (n < 0) 206 if (n < 0)
207 fprintf(stderr, "Ignoring /genproclimit=%s: not a thread count\n", 207 fprintf(stderr, "Ignoring /genproclimit=%s: not a thread count\n",
208 strProcLimit.c_str()); 208 strProcLimit.c_str());
diff --git a/src/net.cpp b/src/net.cpp
index 77f9acf..72a7fda 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1910,7 +1910,7 @@ int StartMinerThreads()
1910 // question anyone asks. 1910 // question anyone asks.
1911 printf("Mining with %d thread(s) of %u core(s) -- about %d MB " 1911 printf("Mining with %d thread(s) of %u core(s) -- about %d MB "
1912 "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", 1912 "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n",
1913 nStarted, std::thread::hardware_concurrency(), 2080 + 2 * nStarted); 1913 nStarted, AvailableCoreCount(), 2080 + 2 * nStarted);
1914 return nStarted; 1914 return nStarted;
1915} 1915}
1916 1916
diff --git a/src/util.h b/src/util.h
index d95d3ae..d72518b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -8,6 +8,13 @@
8#include <cxxabi.h> 8#include <cxxabi.h>
9#endif 9#endif
10 10
11// For AvailableCoreCount() below: sched_getaffinity on Linux, and
12// hardware_concurrency as the fallback everywhere else.
13#include <thread>
14#ifdef __linux__
15#include <sched.h>
16#endif
17
11#if defined(_MSC_VER) || defined(__BORLANDC__) 18#if defined(_MSC_VER) || defined(__BORLANDC__)
12typedef __int64 int64; 19typedef __int64 int64;
13typedef unsigned __int64 uint64; 20typedef unsigned __int64 uint64;
@@ -144,6 +151,34 @@ int64 GetTime();
144int64 GetAdjustedTime(); 151int64 GetAdjustedTime();
145void AddTimeData(unsigned int ip, int64 nTime); 152void AddTimeData(unsigned int ip, int64 nTime);
146 153
154// Cores this process is actually allowed to run on.
155//
156// std::thread::hardware_concurrency() reports the MACHINE's core count, which
157// is not the same number inside a container. With --cpuset-cpus 0-5 on a
158// 12-core host it still answers 12, so every caller that sized a thread pool
159// from it oversubscribed by 2x and then wondered why the box was thrashing:
160// the pool node started 11 miner threads on 6 permitted CPUs, and a burst
161// miner asked for 2 cores filled the ~2 GB RandomX dataset with 12 threads.
162//
163// sched_getaffinity reports what the cpuset permits, which is the number every
164// one of those callers meant. Falls back to hardware_concurrency where the
165// call is unavailable, and never returns 0 -- callers divide by this.
166inline unsigned int AvailableCoreCount()
167{
168#ifdef __linux__
169 cpu_set_t cpuset;
170 CPU_ZERO(&cpuset);
171 if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0)
172 {
173 int nAllowed = CPU_COUNT(&cpuset);
174 if (nAllowed > 0)
175 return (unsigned int)nAllowed;
176 }
177#endif
178 unsigned int nCores = std::thread::hardware_concurrency();
179 return nCores > 0 ? nCores : 1;
180}
181
147 182
148 183
149 184