diff --git a/CHANGELOG.md b/CHANGELOG.md index c868a0da5..fac625109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# v6.6.1 +- Fixed, benchmark validation on NUMA hardware produced incorrect results in some conditions. + # v6.6.0 - Online benchmark protocol upgraded to v2, validation not compatible with previous versions. - Single thread benchmark now is cheat-resistant, not possible speedup it with multiple threads. diff --git a/src/backend/common/benchmark/BenchState.cpp b/src/backend/common/benchmark/BenchState.cpp index 4fd5df828..ec2074dc9 100644 --- a/src/backend/common/benchmark/BenchState.cpp +++ b/src/backend/common/benchmark/BenchState.cpp @@ -33,17 +33,26 @@ namespace xmrig { -static bool done = false; -static std::mutex mutex; -static std::shared_ptr async; -static uint32_t remaining = 0; -static uint64_t doneTime = 0; -static uint64_t result = 0; -static uint64_t topDiff = 0; +class BenchStatePrivate +{ +public: + BenchStatePrivate(IBenchListener *listener, uint32_t size) : + listener(listener), + size(size) + {} -IBenchListener *BenchState::m_listener = nullptr; -uint32_t BenchState::m_size = 0; + IBenchListener *listener; + std::mutex mutex; + std::shared_ptr async; + uint32_t remaining = 0; + uint32_t size; + uint64_t doneTime = 0; +}; + + +static BenchStatePrivate *d_ptr = nullptr; +std::atomic BenchState::m_data{}; } // namespace xmrig @@ -52,7 +61,13 @@ uint32_t BenchState::m_size = 0; bool xmrig::BenchState::isDone() { - return xmrig::done; + return d_ptr == nullptr; +} + + +uint32_t xmrig::BenchState::size() +{ + return d_ptr ? d_ptr->size : 0U; } @@ -71,18 +86,18 @@ uint64_t xmrig::BenchState::referenceHash(const Algorithm &algo, uint32_t size, uint64_t xmrig::BenchState::start(size_t threads, const IBackend *backend) { - assert(m_listener != nullptr); + assert(d_ptr != nullptr); - remaining = static_cast(threads); + d_ptr->remaining = static_cast(threads); - async = std::make_shared([] { - m_listener->onBenchDone(result, topDiff, doneTime); - async.reset(); - xmrig::done = true; + d_ptr->async = std::make_shared([] { + d_ptr->listener->onBenchDone(m_data, 0, d_ptr->doneTime); + + destroy(); }); const uint64_t ts = Chrono::steadyMSecs(); - m_listener->onBenchReady(ts, remaining, backend); + d_ptr->listener->onBenchReady(ts, d_ptr->remaining, backend); return ts; } @@ -90,23 +105,39 @@ uint64_t xmrig::BenchState::start(size_t threads, const IBackend *backend) void xmrig::BenchState::destroy() { - xmrig::done = true; - async.reset(); + delete d_ptr; + d_ptr = nullptr; } -void xmrig::BenchState::done(uint64_t data, uint64_t diff, uint64_t ts) +void xmrig::BenchState::done() { - assert(async && remaining > 0); + assert(d_ptr != nullptr && d_ptr->async && d_ptr->remaining > 0); - std::lock_guard lock(mutex); + const uint64_t ts = Chrono::steadyMSecs(); - result ^= data; - doneTime = std::max(doneTime, ts); - topDiff = std::max(topDiff, diff); - --remaining; + std::lock_guard lock(d_ptr->mutex); - if (remaining == 0) { - async->send(); + d_ptr->doneTime = std::max(d_ptr->doneTime, ts); + --d_ptr->remaining; + + if (d_ptr->remaining == 0) { + d_ptr->async->send(); } } + + +void xmrig::BenchState::init(IBenchListener *listener, uint32_t size) +{ + assert(d_ptr == nullptr); + + d_ptr = new BenchStatePrivate(listener, size); +} + + +void xmrig::BenchState::setSize(uint32_t size) +{ + assert(d_ptr != nullptr); + + d_ptr->size = size; +} diff --git a/src/backend/common/benchmark/BenchState.h b/src/backend/common/benchmark/BenchState.h index e451f4a71..4dd6647f3 100644 --- a/src/backend/common/benchmark/BenchState.h +++ b/src/backend/common/benchmark/BenchState.h @@ -20,6 +20,7 @@ #define XMRIG_BENCHSTATE_H +#include #include #include @@ -36,18 +37,19 @@ class BenchState { public: static bool isDone(); + static uint32_t size(); static uint64_t referenceHash(const Algorithm &algo, uint32_t size, uint32_t threads); static uint64_t start(size_t threads, const IBackend *backend); static void destroy(); - static void done(uint64_t data, uint64_t diff, uint64_t ts); + static void done(); + static void init(IBenchListener *listener, uint32_t size); + static void setSize(uint32_t size); - inline static uint32_t size() { return m_size; } - inline static void setListener(IBenchListener *listener) { m_listener = listener; } - inline static void setSize(uint32_t size) { m_size = size; } + inline static uint64_t data() { return m_data; } + inline static void add(uint64_t value) { m_data.fetch_xor(value, std::memory_order_relaxed); } private: - static IBenchListener *m_listener; - static uint32_t m_size; + static std::atomic m_data; }; diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp index a00b26536..fc6b7e74a 100644 --- a/src/backend/cpu/CpuBackend.cpp +++ b/src/backend/cpu/CpuBackend.cpp @@ -347,13 +347,7 @@ void xmrig::CpuBackend::setJob(const Job &job) const auto &cpu = d_ptr->controller->config()->cpu(); -# ifdef XMRIG_FEATURE_BENCHMARK - const uint32_t benchSize = BenchState::size(); -# else - constexpr uint32_t benchSize = 0; -# endif - - auto threads = cpu.get(d_ptr->controller->miner(), job.algorithm(), benchSize); + auto threads = cpu.get(d_ptr->controller->miner(), job.algorithm()); if (!d_ptr->threads.empty() && d_ptr->threads.size() == threads.size() && std::equal(d_ptr->threads.begin(), d_ptr->threads.end(), threads.begin())) { return; } @@ -370,7 +364,7 @@ void xmrig::CpuBackend::setJob(const Job &job) stop(); # ifdef XMRIG_FEATURE_BENCHMARK - if (benchSize) { + if (BenchState::size()) { d_ptr->benchmark = std::make_shared(threads.size(), this); } # endif diff --git a/src/backend/cpu/CpuConfig.cpp b/src/backend/cpu/CpuConfig.cpp index 5614ef61f..9833a5dab 100644 --- a/src/backend/cpu/CpuConfig.cpp +++ b/src/backend/cpu/CpuConfig.cpp @@ -113,7 +113,7 @@ size_t xmrig::CpuConfig::memPoolSize() const } -std::vector xmrig::CpuConfig::get(const Miner *miner, const Algorithm &algorithm, uint32_t benchSize) const +std::vector xmrig::CpuConfig::get(const Miner *miner, const Algorithm &algorithm) const { std::vector out; const auto &threads = m_threads.get(algorithm); @@ -126,7 +126,7 @@ std::vector xmrig::CpuConfig::get(const Miner *miner, cons out.reserve(count); for (const auto &thread : threads.data()) { - out.emplace_back(miner, algorithm, *this, thread, benchSize, count); + out.emplace_back(miner, algorithm, *this, thread, count); } return out; diff --git a/src/backend/cpu/CpuConfig.h b/src/backend/cpu/CpuConfig.h index 4add65531..95defa67b 100644 --- a/src/backend/cpu/CpuConfig.h +++ b/src/backend/cpu/CpuConfig.h @@ -72,7 +72,7 @@ public: bool isHwAES() const; rapidjson::Value toJSON(rapidjson::Document &doc) const; size_t memPoolSize() const; - std::vector get(const Miner *miner, const Algorithm &algorithm, uint32_t benchSize) const; + std::vector get(const Miner *miner, const Algorithm &algorithm) const; void read(const rapidjson::Value &value); inline bool isEnabled() const { return m_enabled; } diff --git a/src/backend/cpu/CpuLaunchData.cpp b/src/backend/cpu/CpuLaunchData.cpp index 94628c84e..a5e0c99eb 100644 --- a/src/backend/cpu/CpuLaunchData.cpp +++ b/src/backend/cpu/CpuLaunchData.cpp @@ -32,7 +32,7 @@ #include -xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize, size_t threads) : +xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, size_t threads) : algorithm(algorithm), assembly(config.assembly()), astrobwtAVX2(config.astrobwtAVX2()), @@ -44,7 +44,6 @@ xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorit affinity(thread.affinity()), miner(miner), threads(threads), - benchSize(benchSize), intensity(std::min(thread.intensity(), algorithm.maxIntensity())) { } diff --git a/src/backend/cpu/CpuLaunchData.h b/src/backend/cpu/CpuLaunchData.h index 8ba3180f9..8801c005f 100644 --- a/src/backend/cpu/CpuLaunchData.h +++ b/src/backend/cpu/CpuLaunchData.h @@ -44,7 +44,7 @@ class Miner; class CpuLaunchData { public: - CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize, size_t threads); + CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, size_t threads); bool isEqual(const CpuLaunchData &other) const; CnHash::AlgoVariant av() const; @@ -67,7 +67,6 @@ public: const int64_t affinity; const Miner *miner; const size_t threads; - const uint32_t benchSize; const uint32_t intensity; }; diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp index d61e8038f..778f8e465 100644 --- a/src/backend/cpu/CpuWorker.cpp +++ b/src/backend/cpu/CpuWorker.cpp @@ -62,20 +62,6 @@ namespace xmrig { static constexpr uint32_t kReserveCount = 32768; - -template -inline bool nextRound(WorkerJob &job, uint32_t benchSize) -{ - if (!job.nextRound(benchSize ? 1 : kReserveCount, 1)) { - JobResults::done(job.currentJob()); - - return false; - } - - return true; -} - - } // namespace xmrig @@ -92,7 +78,6 @@ xmrig::CpuWorker::CpuWorker(size_t id, const CpuLaunchData &data) : m_astrobwtMaxSize(data.astrobwtMaxSize * 1000), m_miner(data.miner), m_threads(data.threads), - m_benchSize(data.benchSize), m_ctx() { m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages, false, true, m_node); @@ -241,12 +226,12 @@ void xmrig::CpuWorker::start() # ifdef XMRIG_FEATURE_BENCHMARK if (m_benchSize) { if (current_job_nonces[0] >= m_benchSize) { - return BenchState::done(m_benchData, m_benchDiff, Chrono::steadyMSecs());; + return BenchState::done(); } // Make each hash dependent on the previous one in single thread benchmark to prevent cheating with multiple threads if (m_threads == 1) { - *(uint64_t*)(m_job.blob()) ^= m_benchData; + *(uint64_t*)(m_job.blob()) ^= BenchState::data(); } } # endif @@ -260,7 +245,7 @@ void xmrig::CpuWorker::start() randomx_calculate_hash_first(m_vm, tempHash, m_job.blob(), job.size()); } - if (!nextRound(m_job, m_benchSize)) { + if (!nextRound()) { break; } @@ -280,7 +265,7 @@ void xmrig::CpuWorker::start() fn(job.algorithm())(m_job.blob(), job.size(), m_hash, m_ctx, job.height()); } - if (!nextRound(m_job, m_benchSize)) { + if (!nextRound()) { break; }; } @@ -292,8 +277,7 @@ void xmrig::CpuWorker::start() # ifdef XMRIG_FEATURE_BENCHMARK if (m_benchSize) { if (current_job_nonces[i] < m_benchSize) { - m_benchData ^= value; - m_benchDiff = std::max(m_benchDiff, Job::toDiff(value)); + BenchState::add(value); } } else @@ -315,6 +299,25 @@ void xmrig::CpuWorker::start() } +template +bool xmrig::CpuWorker::nextRound() +{ +# ifdef XMRIG_FEATURE_BENCHMARK + const uint32_t count = m_benchSize ? 1U : kReserveCount; +# else + constexpr uint32_t count = kReserveCount; +# endif + + if (!m_job.nextRound(count, 1)) { + JobResults::done(m_job.currentJob()); + + return false; + } + + return true; +} + + template bool xmrig::CpuWorker::verify(const Algorithm &algorithm, const uint8_t *referenceValue) { @@ -395,12 +398,17 @@ void xmrig::CpuWorker::consumeJob() return; } - m_job.add(m_miner->job(), m_benchSize ? 1 : kReserveCount, Nonce::CPU); + auto job = m_miner->job(); # ifdef XMRIG_FEATURE_BENCHMARK - m_benchData = 0; + m_benchSize = job.benchSize(); + const uint32_t count = m_benchSize ? 1U : kReserveCount; +# else + constexpr uint32_t count = kReserveCount; # endif + m_job.add(job, count, Nonce::CPU); + # ifdef XMRIG_ALGO_RANDOMX if (m_job.currentJob().algorithm().family() == Algorithm::RANDOM_X) { allocateRandomX_VM(); diff --git a/src/backend/cpu/CpuWorker.h b/src/backend/cpu/CpuWorker.h index 6669f5db3..06c92fa46 100644 --- a/src/backend/cpu/CpuWorker.h +++ b/src/backend/cpu/CpuWorker.h @@ -68,6 +68,7 @@ private: void allocateRandomX_VM(); # endif + bool nextRound(); bool verify(const Algorithm &algorithm, const uint8_t *referenceValue); bool verify2(const Algorithm &algorithm, const uint8_t *referenceValue); void allocateCnCtx(); @@ -83,18 +84,16 @@ private: const int m_astrobwtMaxSize; const Miner *m_miner; const size_t m_threads; - const uint32_t m_benchSize; cryptonight_ctx *m_ctx[N]; VirtualMemory *m_memory = nullptr; WorkerJob m_job; # ifdef XMRIG_ALGO_RANDOMX - randomx_vm *m_vm = nullptr; + randomx_vm *m_vm = nullptr; # endif # ifdef XMRIG_FEATURE_BENCHMARK - uint64_t m_benchData = 0; - uint64_t m_benchDiff = 0; + uint32_t m_benchSize = 0; # endif }; diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp index fc17b5102..203454143 100644 --- a/src/base/net/stratum/Job.cpp +++ b/src/base/net/stratum/Job.cpp @@ -174,6 +174,10 @@ void xmrig::Job::copy(const Job &other) memcpy(m_rawBlob, other.m_rawBlob, sizeof(m_rawBlob)); memcpy(m_rawTarget, other.m_rawTarget, sizeof(m_rawTarget)); # endif + +# ifdef XMRIG_FEATURE_BENCHMARK + m_benchSize = other.m_benchSize; +# endif } @@ -205,4 +209,8 @@ void xmrig::Job::move(Job &&other) memcpy(m_rawBlob, other.m_rawBlob, sizeof(m_rawBlob)); memcpy(m_rawTarget, other.m_rawTarget, sizeof(m_rawTarget)); # endif + +# ifdef XMRIG_FEATURE_BENCHMARK + m_benchSize = other.m_benchSize; +# endif } diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h index 761b5af22..5621cbc7f 100644 --- a/src/base/net/stratum/Job.h +++ b/src/base/net/stratum/Job.h @@ -111,6 +111,11 @@ public: inline Job &operator=(const Job &other) { copy(other); return *this; } inline Job &operator=(Job &&other) noexcept { move(std::move(other)); return *this; } +# ifdef XMRIG_FEATURE_BENCHMARK + inline uint32_t benchSize() const { return m_benchSize; } + inline void setBenchSize(uint32_t size) { m_benchSize = size; } +# endif + private: void copy(const Job &other); void move(Job &&other); @@ -135,6 +140,10 @@ private: char m_rawTarget[24]{}; String m_rawSeedHash; # endif + +# ifdef XMRIG_FEATURE_BENCHMARK + uint32_t m_benchSize = 0; +# endif }; diff --git a/src/base/net/stratum/benchmark/BenchClient.cpp b/src/base/net/stratum/benchmark/BenchClient.cpp index 71d6a183e..003eeca73 100644 --- a/src/base/net/stratum/benchmark/BenchClient.cpp +++ b/src/base/net/stratum/benchmark/BenchClient.cpp @@ -51,8 +51,7 @@ xmrig::BenchClient::BenchClient(const std::shared_ptr &benchmark, I blob[Job::kMaxSeedSize * 2] = '\0'; m_job.setSeedHash(blob.data()); - BenchState::setListener(this); - BenchState::setSize(m_benchmark->size()); + BenchState::init(this, m_benchmark->size()); # ifdef XMRIG_FEATURE_HTTP if (m_benchmark->isSubmit()) { @@ -75,6 +74,9 @@ xmrig::BenchClient::BenchClient(const std::shared_ptr &benchmark, I return; } + + m_job.setBenchSize(m_benchmark->size()); + } @@ -219,6 +221,8 @@ bool xmrig::BenchClient::setSeed(const char *seed) return false; } + m_job.setBenchSize(BenchState::size()); + LOG_NOTICE("%s " WHITE_BOLD("seed ") BLACK_BOLD("%s"), tag(), seed); return true; @@ -286,11 +290,11 @@ void xmrig::BenchClient::onGetReply(const rapidjson::Value &value) m_hash = strtoull(hash, nullptr, 16); } + BenchState::setSize(Json::getUint(value, BenchConfig::kSize)); + m_job.setAlgorithm(Json::getString(value, BenchConfig::kAlgo)); setSeed(Json::getString(value, BenchConfig::kSeed)); - BenchState::setSize(Json::getUint(value, BenchConfig::kSize)); - start(); } diff --git a/src/version.h b/src/version.h index 47ac86ec7..384f7df11 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ #define APP_ID "xmrig" #define APP_NAME "XMRig" #define APP_DESC "XMRig miner" -#define APP_VERSION "6.6.0" +#define APP_VERSION "6.6.1-dev" #define APP_DOMAIN "xmrig.com" #define APP_SITE "www.xmrig.com" #define APP_COPYRIGHT "Copyright (C) 2016-2020 xmrig.com" @@ -36,7 +36,7 @@ #define APP_VER_MAJOR 6 #define APP_VER_MINOR 6 -#define APP_VER_PATCH 0 +#define APP_VER_PATCH 1 #ifdef _MSC_VER # if (_MSC_VER >= 1920)