diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui.cpp | 3 | ||||
| -rw-r--r-- | src/main.cpp | 51 | ||||
| -rw-r--r-- | src/main.h | 8 | ||||
| -rw-r--r-- | src/main_gui.cpp | 25 | ||||
| -rw-r--r-- | src/net.cpp | 58 | ||||
| -rw-r--r-- | src/net.h | 2 |
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(); | |||
| 32 | string PubKeyToAddress(const std::vector<unsigned char>& vchPubKey); | 32 | string PubKeyToAddress(const std::vector<unsigned char>& vchPubKey); |
| 33 | bool AddressToHash160(const std::string& str, uint160& hash160Ret); | 33 | bool AddressToHash160(const std::string& str, uint160& hash160Ret); |
| 34 | void ThreadBitcoinMiner(void*); | 34 | void ThreadBitcoinMiner(void*); |
| 35 | int StartMinerThreads(); | ||
| 35 | void ThreadRPCServer(void*); | 36 | void 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 |
| 74 | int fGenerateBitcoins; | 74 | int 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. | ||
| 81 | int nMinerThreads = 0; | ||
| 75 | int64 nTransactionFee = 0; | 82 | int64 nTransactionFee = 0; |
| 76 | CAddress addrIncoming; | 83 | CAddress addrIncoming; |
| 77 | int nMineMode = MINE_RELAY; | 84 | int 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. | ||
| 139 | int 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 | |||
| 122 | bool AddKey(const CKey& key) | 149 | bool 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 | ||
| 2772 | bool BitcoinMiner() | 2799 | bool 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; |
| @@ -73,6 +73,10 @@ extern string strPoolName; // operator: announced pool name | |||
| 73 | extern string strPoolDashboardUrl; // operator: optional dashboard URL | 73 | extern string strPoolDashboardUrl; // operator: optional dashboard URL |
| 74 | extern double dPoolFeePercent; // operator: announced fee percent | 74 | extern double dPoolFeePercent; // operator: announced fee percent |
| 75 | extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) | 75 | extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) |
| 76 | // Threads to hash with; 0 = decide from the hardware. Set by /genproclimit. | ||
| 77 | extern int nMinerThreads; | ||
| 78 | // Resolves the automatic case into a real count. | ||
| 79 | int MinerThreadCount(); | ||
| 76 | extern bool gPoolServerRunning; // GUI/startup state | 80 | extern bool gPoolServerRunning; // GUI/startup state |
| 77 | extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp) | 81 | extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp) |
| 78 | 82 | ||
| @@ -117,7 +121,9 @@ void PrintBlockTree(); | |||
| 117 | void AddOrphanTx(const CDataStream& vMsg); | 121 | void AddOrphanTx(const CDataStream& vMsg); |
| 118 | void EraseOrphanTx(uint256 hash); | 122 | void EraseOrphanTx(uint256 hash); |
| 119 | void LimitOrphanTx(unsigned int nMaxOrphans); | 123 | void LimitOrphanTx(unsigned int nMaxOrphans); |
| 120 | bool 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. | ||
| 126 | bool BitcoinMiner(int nThreadId = 1); | ||
| 121 | void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server | 127 | void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server |
| 122 | void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate); | 128 | void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate); |
| 123 | void SetParticipantMiningStatus(const std::string& status); | 129 | void 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. | ||
| 1239 | static CCriticalSection cs_nMinersRunning; | ||
| 1240 | static int nMinersRunning = 0; | ||
| 1241 | |||
| 1228 | void ThreadBitcoinMiner(void* parg) | 1242 | void 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. | ||
| 1267 | int 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 | ||
| @@ -58,6 +58,8 @@ int BtfPexAccept(const std::vector<std::string>& vDesc); | |||
| 58 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); | 58 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); |
| 59 | bool AnySubscribed(unsigned int nChannel); | 59 | bool AnySubscribed(unsigned int nChannel); |
| 60 | void ThreadBitcoinMiner(void* parg); | 60 | void ThreadBitcoinMiner(void* parg); |
| 61 | // Starts MinerThreadCount() miners; returns how many came up. | ||
| 62 | int StartMinerThreads(); | ||
| 61 | bool StartNode(string& strError=REF(string())); | 63 | bool StartNode(string& strError=REF(string())); |
| 62 | bool StopNode(); | 64 | bool StopNode(); |
| 63 | void CheckForShutdown(int n); | 65 | void CheckForShutdown(int n); |