diff options
Diffstat (limited to 'src/randomx_pow.cpp')
| -rw-r--r-- | src/randomx_pow.cpp | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/src/randomx_pow.cpp b/src/randomx_pow.cpp index 17bf455..d617ba2 100644 --- a/src/randomx_pow.cpp +++ b/src/randomx_pow.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <randomx.h> | 7 | #include <randomx.h> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include <mutex> | ||
| 10 | 11 | ||
| 11 | // Fixed key (seed) that determines the RandomX cache/dataset. Keeping it | 12 | // Fixed key (seed) that determines the RandomX cache/dataset. Keeping it |
| 12 | // constant avoids per-epoch "reseed" logic; for the CPU+RAM fairness goal | 13 | // constant avoids per-epoch "reseed" logic; for the CPU+RAM fairness goal |
| @@ -52,10 +53,32 @@ bool RandomXInit() | |||
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | 55 | ||
| 56 | // One dataset, once, no matter how many miners ask for it. | ||
| 57 | // | ||
| 58 | // Every miner thread calls this at startup. The g_fFast guard below is only | ||
| 59 | // set at the very end, after the ~2 GB dataset has been filled, which takes | ||
| 60 | // tens of seconds -- so without this lock all of them sail past the guard | ||
| 61 | // together and each allocates its own dataset. Measured in production: a | ||
| 62 | // 32-core machine logged "initializing ~2 GB dataset" 34 times and committed | ||
| 63 | // 65 GB of private pages against a startup message promising 2142 MB. | ||
| 64 | // | ||
| 65 | // The waste was the mild half. g_dataset is assigned the moment the memory is | ||
| 66 | // allocated, before it holds anything, so the last thread to allocate would | ||
| 67 | // swing the pointer out from under the threads still filling their own. A | ||
| 68 | // miner could then hash against a dataset another thread had not finished | ||
| 69 | // writing, producing proof of work that no other node can reproduce. | ||
| 70 | // | ||
| 71 | // Holding the lock across the whole initialisation is deliberate: a thread | ||
| 72 | // that arrives mid-init has nothing useful to do until the dataset exists, | ||
| 73 | // and blocking is how it waits for exactly that. | ||
| 74 | static std::mutex g_csDataset; | ||
| 75 | |||
| 55 | bool RandomXInitDataset(int nThreads) | 76 | bool RandomXInitDataset(int nThreads) |
| 56 | { | 77 | { |
| 57 | if (!g_fInit && !RandomXInit()) | 78 | if (!g_fInit && !RandomXInit()) |
| 58 | return false; | 79 | return false; |
| 80 | |||
| 81 | std::lock_guard<std::mutex> lock(g_csDataset); | ||
| 59 | if (g_fFast) | 82 | if (g_fFast) |
| 60 | return true; | 83 | return true; |
| 61 | 84 | ||