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:
Diffstat (limited to 'src')
-rw-r--r--src/gui.cpp3
-rw-r--r--src/main.cpp51
-rw-r--r--src/main.h8
-rw-r--r--src/main_gui.cpp25
-rw-r--r--src/net.cpp58
-rw-r--r--src/net.h2
6 files changed, 131 insertions, 16 deletions
diff --git a/src/gui.cpp b/src/gui.cpp
index fc6f1e1..825202b 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -32,6 +32,7 @@ int64 GetBalance();
32string PubKeyToAddress(const std::vector<unsigned char>& vchPubKey); 32string PubKeyToAddress(const std::vector<unsigned char>& vchPubKey);
33bool AddressToHash160(const std::string& str, uint160& hash160Ret); 33bool AddressToHash160(const std::string& str, uint160& hash160Ret);
34void ThreadBitcoinMiner(void*); 34void ThreadBitcoinMiner(void*);
35int StartMinerThreads();
35void ThreadRPCServer(void*); 36void ThreadRPCServer(void*);
36 37
37// --------------------------------------------------------------------------- 38// ---------------------------------------------------------------------------
@@ -762,7 +763,7 @@ static void DrawOptionsDialog()
762 fGenerateBitcoins = 0; 763 fGenerateBitcoins = 0;
763 } else { 764 } else {
764 fGenerateBitcoins = 1; 765 fGenerateBitcoins = 1;
765 if (!vfThreadRunning[3]) _beginthread(ThreadBitcoinMiner, 0, NULL); 766 if (!vfThreadRunning[3]) StartMinerThreads();
766 } 767 }
767 768
768 if (nMineMode == MINE_OPERATOR) { 769 if (nMineMode == MINE_OPERATOR) {
diff --git a/src/main.cpp b/src/main.cpp
index c882b0b..d4fc6d0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -72,6 +72,13 @@ bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local t
72 72
73// Settings 73// Settings
74int fGenerateBitcoins; 74int fGenerateBitcoins;
75// How many threads to hash with. 0 means "decide from the hardware" -- see
76// MinerThreadCount(). This was effectively 1 forever, because Bitcoin 0.1.0
77// started exactly one miner thread and nothing here ever changed that. It made
78// sense for SHA-256d in 2009; RandomX is built to be fed by every core at once,
79// so a 32-core machine was mining at a thirty-second of its capacity and the
80// only visible symptom was a suspiciously idle CPU.
81int nMinerThreads = 0;
75int64 nTransactionFee = 0; 82int64 nTransactionFee = 0;
76CAddress addrIncoming; 83CAddress addrIncoming;
77int nMineMode = MINE_RELAY; 84int nMineMode = MINE_RELAY;
@@ -119,6 +126,26 @@ void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, doubl
119// mapKeys 126// mapKeys
120// 127//
121 128
129// Threads to hash with, resolving the automatic case.
130//
131// Automatic leaves one core to the rest of the node -- accepting blocks,
132// talking to peers, serving the GUI -- because a machine that mines perfectly
133// while falling behind the chain has won nothing. Anyone who disagrees can say
134// so with /genproclimit and get exactly what they ask for.
135//
136// Memory is not the reason to hold back: the ~2 GB RandomX dataset is shared by
137// every thread, and each one adds only its own 2 MB scratchpad. Sixteen threads
138// cost about 32 MB more than one.
139int MinerThreadCount()
140{
141 if (nMinerThreads > 0)
142 return nMinerThreads;
143 unsigned int nCores = std::thread::hardware_concurrency();
144 if (nCores <= 1)
145 return 1;
146 return (int)(nCores - 1);
147}
148
122bool AddKey(const CKey& key) 149bool AddKey(const CKey& key)
123{ 150{
124 CRITICAL_BLOCK(cs_mapKeys) 151 CRITICAL_BLOCK(cs_mapKeys)
@@ -2769,7 +2796,7 @@ static bool PoolParticipantMiner()
2769 return true; 2796 return true;
2770} 2797}
2771 2798
2772bool BitcoinMiner() 2799bool BitcoinMiner(int nThreadId)
2773{ 2800{
2774 // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here 2801 // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here
2775 // since the GUI hides Start Mining for Relay, but guard it regardless.) 2802 // since the GUI hides Start Mining for Relay, but guard it regardless.)
@@ -2809,11 +2836,17 @@ bool BitcoinMiner()
2809 while (fGenerateBitcoins) 2836 while (fGenerateBitcoins)
2810 { 2837 {
2811 Sleep(50); 2838 Sleep(50);
2812 CheckForShutdown(3); 2839 // Return instead of _endthread()ing. With more than one miner thread the
2840 // bookkeeping in ThreadBitcoinMiner has to run on the way out, and
2841 // _endthread() does not unwind on Windows, so a normal return is the
2842 // only exit that works the same on both platforms.
2843 if (fShutdown)
2844 return true;
2813 while (vNodes.empty() && !fSoloMineTest) 2845 while (vNodes.empty() && !fSoloMineTest)
2814 { 2846 {
2815 Sleep(1000); 2847 Sleep(1000);
2816 CheckForShutdown(3); 2848 if (fShutdown)
2849 return true;
2817 } 2850 }
2818 2851
2819 unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; 2852 unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
@@ -2908,8 +2941,11 @@ bool BitcoinMiner()
2908 Sleep(1000); 2941 Sleep(1000);
2909 continue; 2942 continue;
2910 } 2943 }
2911 LogPrint("net", "BitcoinMiner: mining with RandomX (%s)\n", 2944 // The thread number is in the line on purpose: every miner would
2912 RandomXFastReady() ? "fast 2GB" : "light 256MB"); 2945 // otherwise print the same text, and the dedup filter would fold them
2946 // into one, leaving no way to tell four threads from one.
2947 LogPrint("net", "BitcoinMiner: thread %d hashing with RandomX (%s)\n",
2948 nThreadId, RandomXFastReady() ? "fast 2GB" : "light 256MB");
2913 2949
2914 unsigned int nStart = GetTime(); 2950 unsigned int nStart = GetTime();
2915 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); 2951 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
@@ -2950,7 +2986,8 @@ bool BitcoinMiner()
2950 // ~256 hashes is already plenty of time) 2986 // ~256 hashes is already plenty of time)
2951 if ((++pblock->nNonce & 0xff) == 0) 2987 if ((++pblock->nNonce & 0xff) == 0)
2952 { 2988 {
2953 CheckForShutdown(3); 2989 if (fShutdown)
2990 break; // falls through to RandomXDestroyMinerVM below
2954 if (pblock->nNonce == 0) 2991 if (pblock->nNonce == 0)
2955 break; 2992 break;
2956 if (pindexPrev != pindexBest) 2993 if (pindexPrev != pindexBest)
@@ -2963,6 +3000,8 @@ bool BitcoinMiner()
2963 } 3000 }
2964 } 3001 }
2965 RandomXDestroyMinerVM(rxvm); 3002 RandomXDestroyMinerVM(rxvm);
3003 if (fShutdown)
3004 break;
2966 } 3005 }
2967 3006
2968 return true; 3007 return true;
diff --git a/src/main.h b/src/main.h
index 7e5bdf2..750d3f1 100644
--- a/src/main.h
+++ b/src/main.h
@@ -73,6 +73,10 @@ extern string strPoolName; // operator: announced pool name
73extern string strPoolDashboardUrl; // operator: optional dashboard URL 73extern string strPoolDashboardUrl; // operator: optional dashboard URL
74extern double dPoolFeePercent; // operator: announced fee percent 74extern double dPoolFeePercent; // operator: announced fee percent
75extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) 75extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test)
76// Threads to hash with; 0 = decide from the hardware. Set by /genproclimit.
77extern int nMinerThreads;
78// Resolves the automatic case into a real count.
79int MinerThreadCount();
76extern bool gPoolServerRunning; // GUI/startup state 80extern bool gPoolServerRunning; // GUI/startup state
77extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp) 81extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp)
78 82
@@ -117,7 +121,9 @@ void PrintBlockTree();
117void AddOrphanTx(const CDataStream& vMsg); 121void AddOrphanTx(const CDataStream& vMsg);
118void EraseOrphanTx(uint256 hash); 122void EraseOrphanTx(uint256 hash);
119void LimitOrphanTx(unsigned int nMaxOrphans); 123void LimitOrphanTx(unsigned int nMaxOrphans);
120bool BitcoinMiner(); 124// nThreadId is 1..N, only for logging: it makes each miner's lines
125// distinguishable, which the dedup filter would otherwise collapse into one.
126bool BitcoinMiner(int nThreadId = 1);
121void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server 127void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server
122void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate); 128void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate);
123void SetParticipantMiningStatus(const std::string& status); 129void SetParticipantMiningStatus(const std::string& status);
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.
diff --git a/src/net.cpp b/src/net.cpp
index 2581578..9ee056f 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -10,6 +10,7 @@
10#undef snprintf 10#undef snprintf
11#endif 11#endif
12#include "headers.h" 12#include "headers.h"
13#include <thread> // hardware_concurrency, for the miner thread count
13#ifdef _WIN32 14#ifdef _WIN32
14#include <winsock2.h> 15#include <winsock2.h>
15#endif 16#endif
@@ -1224,18 +1225,63 @@ void ThreadMessageHandler2(void* parg)
1224 1225
1225 1226
1226 1227
1227//// todo: start one thread per processor, use getenv("NUMBER_OF_PROCESSORS") 1228// Satoshi's "todo: start one thread per processor" stood here since 2009 and is
1229// what MinerThreadCount() and StartMinerThreads() below finally do -- by way of
1230// std::thread::hardware_concurrency rather than NUMBER_OF_PROCESSORS, which only
1231// ever existed on Windows.
1232//
1233// Miners share slot 3 of vfThreadRunning, and StopNode waits for every slot to
1234// go false before the node tears the database down. With one miner that was the
1235// same thing as "the miner finished"; with several, the first one out would
1236// announce that everybody had finished while the rest were still hashing and
1237// still calling ProcessBlock. So count them, and let only the last one leaving
1238// clear the flag.
1239static CCriticalSection cs_nMinersRunning;
1240static int nMinersRunning = 0;
1241
1228void ThreadBitcoinMiner(void* parg) 1242void ThreadBitcoinMiner(void* parg)
1229{ 1243{
1230 vfThreadRunning[3] = true; 1244 int nThreadId = (int)(intptr_t)parg;
1231 CheckForShutdown(3); 1245 CRITICAL_BLOCK(cs_nMinersRunning)
1246 {
1247 nMinersRunning++;
1248 vfThreadRunning[3] = true;
1249 }
1232 try 1250 try
1233 { 1251 {
1234 bool fRet = BitcoinMiner(); 1252 bool fRet = BitcoinMiner(nThreadId);
1235 printf("BitcoinMiner returned %s\n\n\n", fRet ? "true" : "false"); 1253 printf("BitcoinMiner thread %d returned %s\n", nThreadId, fRet ? "true" : "false");
1236 } 1254 }
1237 CATCH_PRINT_EXCEPTION("BitcoinMiner()") 1255 CATCH_PRINT_EXCEPTION("BitcoinMiner()")
1238 vfThreadRunning[3] = false; 1256 CRITICAL_BLOCK(cs_nMinersRunning)
1257 {
1258 if (--nMinersRunning <= 0)
1259 {
1260 nMinersRunning = 0;
1261 vfThreadRunning[3] = false;
1262 }
1263 }
1264}
1265
1266// Start the miners. Returns how many actually got off the ground.
1267int StartMinerThreads()
1268{
1269 int nThreads = MinerThreadCount();
1270 int nStarted = 0;
1271 for (int i = 0; i < nThreads; i++)
1272 {
1273 if (_beginthread(ThreadBitcoinMiner, 0, (void*)(intptr_t)(i + 1)) == (uintptr_t)-1)
1274 printf("Error: _beginthread(ThreadBitcoinMiner) failed on thread %d\n", i + 1);
1275 else
1276 nStarted++;
1277 }
1278 // The dataset is one allocation shared by all of them; only the 2 MB
1279 // scratchpads multiply. Printed because "why is it using 2 GB" is the first
1280 // question anyone asks.
1281 printf("Mining with %d thread(s) of %u core(s) -- about %d MB "
1282 "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n",
1283 nStarted, std::thread::hardware_concurrency(), 2080 + 2 * nStarted);
1284 return nStarted;
1239} 1285}
1240 1286
1241 1287
diff --git a/src/net.h b/src/net.h
index 4379d04..11c023e 100644
--- a/src/net.h
+++ b/src/net.h
@@ -58,6 +58,8 @@ int BtfPexAccept(const std::vector<std::string>& vDesc);
58void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); 58void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);
59bool AnySubscribed(unsigned int nChannel); 59bool AnySubscribed(unsigned int nChannel);
60void ThreadBitcoinMiner(void* parg); 60void ThreadBitcoinMiner(void* parg);
61// Starts MinerThreadCount() miners; returns how many came up.
62int StartMinerThreads();
61bool StartNode(string& strError=REF(string())); 63bool StartNode(string& strError=REF(string()));
62bool StopNode(); 64bool StopNode();
63void CheckForShutdown(int n); 65void CheckForShutdown(int n);