diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-19 22:46:53 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 04:37:36 +0000 |
| commit | c4c19bd894c8fc30c558e560e326aab538b23170 (patch) | |
| tree | cdd9be186345f593ca1aa7e1f268081ed6c369ff | |
| parent | 8f8a02e8ea3358ddec043bb9b1f615d0d1fc9fc7 (diff) | |
mine: one pool session in participant mode, not one per core
StartMinerThreads() sizes its pool with MinerThreadCount(), which is right for
solo mining -- each thread hashes a template of its own -- and wrong for
participant mode, where every thread entering BitcoinMiner() is a whole miner:
its own Stratum session, its own payout address off the rotation, its own pool
of /hashthreads workers. The two counts multiplied instead of dividing.
Measured on the pool box with /hashthreads=2: a container with no cpuset logged
"Mining with 11 thread(s) of 12 core(s)" -- 11 sessions, 22 hash threads on 12
cores, 1058% CPU, load 22.68 -- while the same binary under --cpuset-cpus 10,11
logged one session and 199%. The second miner (8 cores, /hashthreads=1) ran 7
sessions and went 74C to 89C. No code differed between those runs. Only the
cpuset did, which means the cpuset was holding the thread count down by
accident and every deployment without one was oversubscribed.
Participant mode now starts exactly one session, so /hashthreads means what it
says instead of being multiplied by the core count. The worker count moves to
ParticipantHashThreadCount(), next to MinerThreadCount() where the two are
easy to confuse, so what is printed at startup and what actually hashes cannot
drift apart -- and it IS printed now. It was only ever visible under -debug,
which is how eleven sessions ran unnoticed.
Same host, same cpuset (0-3), same arguments, measured as a delta over 20 s
more than four minutes after start:
before 399% CPU, 6-7 threads hot, 3 sessions
after 201% CPU, exactly 2 threads at 99.9%, 1 session
Solo mining is untouched: /gen without /participant still starts
MinerThreadCount() threads.
(cherry picked from commit 45bc9ed5bc132712092f3e7902fef746c6cae898)
| -rw-r--r-- | src/main.cpp | 32 | ||||
| -rw-r--r-- | src/main.h | 3 | ||||
| -rw-r--r-- | src/net.cpp | 37 |
3 files changed, 61 insertions, 11 deletions
diff --git a/src/main.cpp b/src/main.cpp index efff96c..77aba89 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -170,6 +170,26 @@ int MinerThreadCount() | |||
| 170 | return (int)(nCores - 1); | 170 | return (int)(nCores - 1); |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | // Hash threads one participant session runs. | ||
| 174 | // | ||
| 175 | // Explicit /hashthreads=N is honored exactly, including 1 -- a miner who says | ||
| 176 | // one means one. Automatic leaves a core to the node, the Stratum I/O loop and | ||
| 177 | // everything else the process still has to do, for the same reason | ||
| 178 | // MinerThreadCount() does. | ||
| 179 | // | ||
| 180 | // Lives here, next to that function, because the two are easy to confuse and | ||
| 181 | // StartMinerThreads() needs to be able to tell them apart: only ONE session | ||
| 182 | // runs in participant mode, and this is what sizes it. | ||
| 183 | int ParticipantHashThreadCount() | ||
| 184 | { | ||
| 185 | if (nHashThreads > 0) | ||
| 186 | return nHashThreads; | ||
| 187 | int nWorkers = (int)AvailableCoreCount(); | ||
| 188 | if (nWorkers < 2) | ||
| 189 | nWorkers = 2; | ||
| 190 | return nWorkers - 1; | ||
| 191 | } | ||
| 192 | |||
| 173 | bool AddKey(const CKey& key) | 193 | bool AddKey(const CKey& key) |
| 174 | { | 194 | { |
| 175 | if (IsWalletEncrypted()) | 195 | if (IsWalletEncrypted()) |
| @@ -3828,13 +3848,11 @@ connected: | |||
| 3828 | // shared dataset), then run the I/O loop below: receive pool messages, | 3848 | // shared dataset), then run the I/O loop below: receive pool messages, |
| 3829 | // publish jobs to the threads, submit the shares they find. | 3849 | // publish jobs to the threads, submit the shares they find. |
| 3830 | // ------------------------------------------------------------------ | 3850 | // ------------------------------------------------------------------ |
| 3831 | int nWorkers = nHashThreads > 0 ? nHashThreads | 3851 | // One count, decided in one place (main.cpp:MinerThreadCount's neighbour), |
| 3832 | : (int)AvailableCoreCount(); | 3852 | // so that what StartMinerThreads() reports at startup and what actually |
| 3833 | if (nHashThreads <= 0) { | 3853 | // hashes here can never drift apart. Explicit /hashthreads=N is honored |
| 3834 | if (nWorkers < 2) nWorkers = 2; | 3854 | // exactly, including 1; automatic leaves one core for node + I/O threads. |
| 3835 | nWorkers -= 1; // leave one core for node + I/O threads | 3855 | int nWorkers = ParticipantHashThreadCount(); |
| 3836 | } | ||
| 3837 | // explicit /hashthreads=N is honored exactly (including 1) | ||
| 3838 | ParticipantJobShared jobShared; | 3856 | ParticipantJobShared jobShared; |
| 3839 | std::vector<ParticipantShareHit> shareHits; | 3857 | std::vector<ParticipantShareHit> shareHits; |
| 3840 | std::mutex shareHitsMtx; | 3858 | std::mutex shareHitsMtx; |
| @@ -103,6 +103,9 @@ extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local te | |||
| 103 | extern int nMinerThreads; | 103 | extern int nMinerThreads; |
| 104 | // Resolves the automatic case into a real count. | 104 | // Resolves the automatic case into a real count. |
| 105 | int MinerThreadCount(); | 105 | int MinerThreadCount(); |
| 106 | // Hash threads one participant session runs; resolves /hashthreads. Not the | ||
| 107 | // same number as MinerThreadCount(), and only ONE session ever runs. | ||
| 108 | int ParticipantHashThreadCount(); | ||
| 106 | extern bool gPoolServerRunning; // GUI/startup state | 109 | extern bool gPoolServerRunning; // GUI/startup state |
| 107 | extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp) | 110 | extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp) |
| 108 | 111 | ||
diff --git a/src/net.cpp b/src/net.cpp index e08f6f7..34c608c 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -1801,7 +1801,21 @@ void ThreadBitcoinMiner(void* parg) | |||
| 1801 | // Start the miners. Returns how many actually got off the ground. | 1801 | // Start the miners. Returns how many actually got off the ground. |
| 1802 | int StartMinerThreads() | 1802 | int StartMinerThreads() |
| 1803 | { | 1803 | { |
| 1804 | int nThreads = MinerThreadCount(); | 1804 | // MinerThreadCount() sizes the SOLO miner, where each thread hashes a |
| 1805 | // template of its own and more threads is simply more hashrate. | ||
| 1806 | // | ||
| 1807 | // Participant mode is not that shape. Every thread that enters | ||
| 1808 | // BitcoinMiner() takes the MINE_PARTICIPANT branch, and that branch is a | ||
| 1809 | // whole miner: its own Stratum session to the pool, its own payout address | ||
| 1810 | // from the rotation, and its own pool of /hashthreads worker threads. So | ||
| 1811 | // the two counts multiply instead of dividing, and the machine ends up | ||
| 1812 | // running MinerThreadCount() x nHashThreads hashing threads plus one pool | ||
| 1813 | // connection each. On a 12-core host with /hashthreads=2 that is 11 | ||
| 1814 | // sessions and 22 hash threads -- roughly 2x the cores, which is not more | ||
| 1815 | // work done, only more contention -- and with /hashthreads unset it is 11 | ||
| 1816 | // sessions of 11 workers. One session is the entirety of participant mode; | ||
| 1817 | // /hashthreads is what sizes it, and it can then mean what it says. | ||
| 1818 | int nThreads = (nMineMode == MINE_PARTICIPANT) ? 1 : MinerThreadCount(); | ||
| 1805 | int nStarted = 0; | 1819 | int nStarted = 0; |
| 1806 | for (int i = 0; i < nThreads; i++) | 1820 | for (int i = 0; i < nThreads; i++) |
| 1807 | { | 1821 | { |
| @@ -1813,9 +1827,24 @@ int StartMinerThreads() | |||
| 1813 | // The dataset is one allocation shared by all of them; only the 2 MB | 1827 | // The dataset is one allocation shared by all of them; only the 2 MB |
| 1814 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first | 1828 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first |
| 1815 | // question anyone asks. | 1829 | // question anyone asks. |
| 1816 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | 1830 | // |
| 1817 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | 1831 | // Participant mode reports the workers that session will run, not the one |
| 1818 | nStarted, AvailableCoreCount(), 2080 + 2 * nStarted); | 1832 | // thread that runs it: the hash thread count is the number an operator |
| 1833 | // watches, and it is the number that was wrong here. It is otherwise only | ||
| 1834 | // logged under -debug, which is how 11 sessions ran unnoticed. | ||
| 1835 | if (nMineMode == MINE_PARTICIPANT) | ||
| 1836 | { | ||
| 1837 | int nWorkers = ParticipantHashThreadCount(); | ||
| 1838 | printf("Mining for the pool with %d hash thread(s) of %u core(s) -- about %d MB " | ||
| 1839 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1840 | nWorkers, AvailableCoreCount(), 2080 + 2 * nWorkers); | ||
| 1841 | } | ||
| 1842 | else | ||
| 1843 | { | ||
| 1844 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | ||
| 1845 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1846 | nStarted, AvailableCoreCount(), 2080 + 2 * nStarted); | ||
| 1847 | } | ||
| 1819 | return nStarted; | 1848 | return nStarted; |
| 1820 | } | 1849 | } |
| 1821 | 1850 | ||