From 2ecece7b3d77e5debb592649bf3b7d952abd6aa9 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Wed, 14 Oct 2020 19:45:05 +0200 Subject: [PATCH] Added benchmark and stress test Easy to use and zero configuration embedded benchmark/stress test. --- cmake/flags.cmake | 4 +- doc/BENCHMARK.md | 29 +++++++ src/backend/common/Worker.h | 9 +- src/backend/common/WorkerJob.h | 4 +- src/backend/common/Workers.cpp | 102 +++++++++++++++++------ src/backend/common/Workers.h | 4 +- src/backend/common/interfaces/IBackend.h | 2 +- src/backend/common/interfaces/IWorker.h | 2 + src/backend/cpu/CpuBackend.cpp | 4 +- src/backend/cpu/CpuBackend.h | 2 +- src/backend/cpu/CpuWorker.cpp | 23 ++++- src/backend/cuda/CudaBackend.cpp | 4 +- src/backend/cuda/CudaBackend.h | 2 +- src/backend/opencl/OclBackend.cpp | 4 +- src/backend/opencl/OclBackend.h | 2 +- src/base/base.cmake | 2 + src/base/kernel/config/BaseTransform.cpp | 16 +++- src/base/kernel/interfaces/IConfig.h | 2 + src/base/net/stratum/Job.cpp | 2 + src/base/net/stratum/Job.h | 3 + src/base/net/stratum/NullClient.cpp | 63 ++++++++++++++ src/base/net/stratum/NullClient.h | 77 +++++++++++++++++ src/base/net/stratum/Pool.cpp | 22 ++++- src/base/net/stratum/Pool.h | 6 +- src/core/Miner.cpp | 10 ++- src/core/config/Config_platform.h | 2 + src/core/config/usage.h | 2 + src/net/Network.cpp | 5 +- 28 files changed, 354 insertions(+), 55 deletions(-) create mode 100644 doc/BENCHMARK.md create mode 100644 src/base/net/stratum/NullClient.cpp create mode 100644 src/base/net/stratum/NullClient.h diff --git a/cmake/flags.cmake b/cmake/flags.cmake index 5edad3392..788d9bdce 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -64,8 +64,8 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) set(CMAKE_C_FLAGS_RELEASE "/MT /O2 /Oi /DNDEBUG /GL") set(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Oi /DNDEBUG /GL") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/Ob1 /GL") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Ob1 /GL") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "/Ob1 /Zi") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Ob1 /Zi") add_definitions(/D_CRT_SECURE_NO_WARNINGS) add_definitions(/D_CRT_NONSTDC_NO_WARNINGS) diff --git a/doc/BENCHMARK.md b/doc/BENCHMARK.md new file mode 100644 index 000000000..a05294b40 --- /dev/null +++ b/doc/BENCHMARK.md @@ -0,0 +1,29 @@ +# Embedded benchmark + +You can run with XMRig with the following commands: +``` +xmrig --bench=1M +xmrig --bench=10M +xmrig --bench=1M -a rx/wow +xmrig --bench=10M -a rx/wow +``` +This will run 1 or 10 millions RandomX hashes and print the time it took. First two commands use Monero variant (2 MB per thread, best for Zen2/Zen3 CPUs), second two commands use Wownero variant (1 MB per thread, useful for Intel and 1st gen Zen/Zen+ CPUs). + +Checksum of all the hashes will be also printed to check stability of your hardware: if it's green then it's correct, if it's red then there was hardware error during computation. No Internet connection is required for the benchmark. + +Double check that you see `Huge pages 100%` both for dataset and for all threads, and also check for `msr register values ... has been set successfully` - without this result will be far from the best. Running as administrator is required for MSR and huge pages to be set up properly. + +![Benchmark example](https://i.imgur.com/PST3BYc.png) + +### Benchmark with custom config + +You can run benchmark with any configuration you want. Just start without command line parameteres, use regular config.json and add `"benchmark":"1M",` on the next line after pool url. + +# Stress test + +You can also run continuous stress-test that is as close to the real RandomX mining as possible and doesn't require any configuration: +``` +xmrig --stress +xmrig --stress -a rx/wow +``` +This will require Internet connection and will run indefinitely. \ No newline at end of file diff --git a/src/backend/common/Worker.h b/src/backend/common/Worker.h index b0825811b..cc38450fa 100644 --- a/src/backend/common/Worker.h +++ b/src/backend/common/Worker.h @@ -45,6 +45,8 @@ public: inline const VirtualMemory *memory() const override { return nullptr; } inline size_t id() const override { return m_id; } inline uint64_t rawHashes() const override { return m_count; } + inline uint64_t benchData() const override { return m_benchData; } + inline uint64_t benchDoneTime() const override { return m_benchDoneTime; } void getHashrateData(uint64_t& hashCount, uint64_t& timeStamp) const override; inline void jobEarlyNotification(const Job&) override {} @@ -56,8 +58,11 @@ protected: uint64_t m_hashCount[2] = {}; uint64_t m_timestamp[2] = {}; std::atomic m_index = {}; - uint32_t m_node = 0; - uint64_t m_count = 0; + uint32_t m_node = 0; + uint64_t m_count = 0; + + uint64_t m_benchData = 0; + uint64_t m_benchDoneTime = 0; }; diff --git a/src/backend/common/WorkerJob.h b/src/backend/common/WorkerJob.h index e835ff1de..a37a05fa9 100644 --- a/src/backend/common/WorkerJob.h +++ b/src/backend/common/WorkerJob.h @@ -68,7 +68,7 @@ public: { m_rounds[index()]++; - if ((m_rounds[index()] % rounds) == 0) { + if ((m_rounds[index()] & (rounds - 1)) == 0) { for (size_t i = 0; i < N; ++i) { if (!Nonce::next(index(), nonce(i), rounds * roundSize, nonceMask())) { return false; @@ -130,7 +130,7 @@ inline bool xmrig::WorkerJob<1>::nextRound(uint32_t rounds, uint32_t roundSize) uint32_t* n = nonce(); - if ((m_rounds[index()] % rounds) == 0) { + if ((m_rounds[index()] & (rounds - 1)) == 0) { if (!Nonce::next(index(), n, rounds * roundSize, nonceMask())) { return false; } diff --git a/src/backend/common/Workers.cpp b/src/backend/common/Workers.cpp index 78816c1ef..6a4cf0cad 100644 --- a/src/backend/common/Workers.cpp +++ b/src/backend/common/Workers.cpp @@ -29,8 +29,10 @@ #include "backend/common/Workers.h" #include "backend/cpu/CpuWorker.h" #include "base/io/log/Log.h" +#include "base/io/log/Tags.h" #include "base/tools/Chrono.h" #include "base/tools/Object.h" +#include "core/Miner.h" #ifdef XMRIG_FEATURE_OPENCL @@ -63,6 +65,10 @@ public: Hashrate *hashrate = nullptr; IBackend *backend = nullptr; + + uint32_t bench = 0; + Algorithm benchAlgo = Algorithm::RX_0; + uint64_t startTime = 0; }; @@ -101,6 +107,11 @@ void xmrig::Workers::setBackend(IBackend *backend) template void xmrig::Workers::start(const std::vector &data) { + if (!data.empty()) { + d_ptr->bench = data.front().miner->job().bench(); + d_ptr->benchAlgo = data.front().miner->job().algorithm(); + } + for (const T &item : data) { m_workers.push_back(new Thread(d_ptr->backend, m_workers.size(), item)); } @@ -111,11 +122,12 @@ void xmrig::Workers::start(const std::vector &data) for (Thread *worker : m_workers) { worker->start(Workers::onReady); - // This sleep is important for optimal caching! - // Threads must allocate scratchpads in order so that adjacent cores will use adjacent scratchpads - // Sub-optimal caching can result in up to 0.5% hashrate penalty - std::this_thread::sleep_for(std::chrono::milliseconds(20)); + if (!d_ptr->bench) { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } } + + d_ptr->startTime = Chrono::steadyMSecs(); } @@ -137,55 +149,91 @@ void xmrig::Workers::stop() template -void xmrig::Workers::tick(uint64_t) +static void getHashrateData(xmrig::IWorker* worker, uint64_t& hashCount, uint64_t& timeStamp) +{ + worker->getHashrateData(hashCount, timeStamp); +} + + +template<> +static void getHashrateData(xmrig::IWorker* worker, uint64_t& hashCount, uint64_t&) +{ + hashCount = worker->rawHashes(); +} + + +template +bool xmrig::Workers::tick(uint64_t) { if (!d_ptr->hashrate) { - return; + return true; } + uint64_t timeStamp = Chrono::steadyMSecs(); + bool totalAvailable = true; uint64_t totalHashCount = 0; + uint32_t benchDone = 0; + uint64_t benchData = 0; + uint64_t benchDoneTime = 0; + for (Thread *handle : m_workers) { - if (handle->worker()) { - uint64_t hashCount, timeStamp; - handle->worker()->getHashrateData(hashCount, timeStamp); + IWorker* worker = handle->worker(); + if (worker) { + uint64_t hashCount; + getHashrateData(worker, hashCount, timeStamp); d_ptr->hashrate->add(handle->id() + 1, hashCount, timeStamp); - const uint64_t n = handle->worker()->rawHashes(); + const uint64_t n = worker->rawHashes(); if (n == 0) { totalAvailable = false; } totalHashCount += n; + + if (d_ptr->bench && worker->benchDoneTime()) { + ++benchDone; + benchData ^= worker->benchData(); + if (worker->benchDoneTime() > benchDoneTime) { + benchDoneTime = worker->benchDoneTime(); + } + } } } if (totalAvailable) { d_ptr->hashrate->add(0, totalHashCount, Chrono::steadyMSecs()); } -} + if (d_ptr->bench && (benchDone == m_workers.size())) { + const double dt = (benchDoneTime - d_ptr->startTime) / 1000.0; -template<> -void xmrig::Workers::tick(uint64_t) -{ - if (!d_ptr->hashrate) { - return; - } + static uint64_t hashCheck[Algorithm::MAX][2] = {}; + hashCheck[Algorithm::RX_0][0] = 0x898B6E0431C28A6BULL; + hashCheck[Algorithm::RX_0][1] = 0xB5231262E2792B26ULL; + hashCheck[Algorithm::RX_WOW][0] = 0x0F3E5400B39EA96AULL; + hashCheck[Algorithm::RX_WOW][1] = 0x0F9E00C5A511C200ULL; - const uint64_t timestamp = Chrono::steadyMSecs(); - uint64_t totalHashCount = 0; - for (Thread *handle : m_workers) { - if (handle->worker()) { - const uint64_t hashCount = handle->worker()->rawHashes(); - d_ptr->hashrate->add(handle->id() + 1, hashCount, timestamp); - totalHashCount += hashCount; + int k = -1; + + switch (d_ptr->bench) { + case 1000000: + k = 0; + break; + + case 10000000: + k = 1; + break; } + + const uint64_t checkData = (k >= 0) ? hashCheck[d_ptr->benchAlgo.id()][k] : 0; + const char* color = checkData ? ((benchData == checkData) ? GREEN_BOLD_S : RED_BOLD_S) : BLACK_BOLD_S; + + LOG_INFO("%s Benchmark finished in %.3f seconds, hash sum = %s%016" PRIX64 CLEAR, Tags::miner(), dt, color, benchData); + return false; } - if (totalHashCount > 0) { - d_ptr->hashrate->add(0, totalHashCount, timestamp); - } + return true; } diff --git a/src/backend/common/Workers.h b/src/backend/common/Workers.h index 022009607..28d9501b2 100644 --- a/src/backend/common/Workers.h +++ b/src/backend/common/Workers.h @@ -63,7 +63,7 @@ public: void setBackend(IBackend *backend); void start(const std::vector &data); void stop(); - void tick(uint64_t ticks); + bool tick(uint64_t ticks); void jobEarlyNotification(const Job&); private: @@ -88,8 +88,6 @@ void xmrig::Workers::jobEarlyNotification(const Job& job) template<> IWorker *Workers::create(Thread *handle); -template<> -void Workers::tick(uint64_t); extern template class Workers; diff --git a/src/backend/common/interfaces/IBackend.h b/src/backend/common/interfaces/IBackend.h index 405d876a7..577213d35 100644 --- a/src/backend/common/interfaces/IBackend.h +++ b/src/backend/common/interfaces/IBackend.h @@ -60,7 +60,7 @@ public: virtual void setJob(const Job &job) = 0; virtual void start(IWorker *worker, bool ready) = 0; virtual void stop() = 0; - virtual void tick(uint64_t ticks) = 0; + virtual bool tick(uint64_t ticks) = 0; # ifdef XMRIG_FEATURE_API virtual rapidjson::Value toJSON(rapidjson::Document &doc) const = 0; diff --git a/src/backend/common/interfaces/IWorker.h b/src/backend/common/interfaces/IWorker.h index 17048c645..3645f6a82 100644 --- a/src/backend/common/interfaces/IWorker.h +++ b/src/backend/common/interfaces/IWorker.h @@ -47,6 +47,8 @@ public: virtual size_t id() const = 0; virtual size_t intensity() const = 0; virtual uint64_t rawHashes() const = 0; + virtual uint64_t benchData() const = 0; + virtual uint64_t benchDoneTime() const = 0; virtual void getHashrateData(uint64_t&, uint64_t&) const = 0; virtual void start() = 0; virtual void jobEarlyNotification(const Job&) = 0; diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp index e6678bcab..c3267d17f 100644 --- a/src/backend/cpu/CpuBackend.cpp +++ b/src/backend/cpu/CpuBackend.cpp @@ -387,9 +387,9 @@ void xmrig::CpuBackend::stop() } -void xmrig::CpuBackend::tick(uint64_t ticks) +bool xmrig::CpuBackend::tick(uint64_t ticks) { - d_ptr->workers.tick(ticks); + return d_ptr->workers.tick(ticks); } diff --git a/src/backend/cpu/CpuBackend.h b/src/backend/cpu/CpuBackend.h index 1046ec359..be63f4372 100644 --- a/src/backend/cpu/CpuBackend.h +++ b/src/backend/cpu/CpuBackend.h @@ -63,7 +63,7 @@ protected: void setJob(const Job &job) override; void start(IWorker *worker, bool ready) override; void stop() override; - void tick(uint64_t ticks) override; + bool tick(uint64_t ticks) override; # ifdef XMRIG_FEATURE_API rapidjson::Value toJSON(rapidjson::Document &doc) const override; diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp index ec571001b..5211bed24 100644 --- a/src/backend/cpu/CpuWorker.cpp +++ b/src/backend/cpu/CpuWorker.cpp @@ -29,6 +29,7 @@ #include "backend/cpu/CpuWorker.h" +#include "base/tools/Chrono.h" #include "core/Miner.h" #include "crypto/cn/CnCtx.h" #include "crypto/cn/CryptoNight_test.h" @@ -59,8 +60,10 @@ static constexpr uint32_t kReserveCount = 32768; template inline bool nextRound(WorkerJob &job) { - if (!job.nextRound(kReserveCount, 1)) { - JobResults::done(job.currentJob()); + const Job& curJob = job.currentJob(); + const uint32_t bench = curJob.bench(); + if (!job.nextRound(bench ? 1 : kReserveCount, 1)) { + JobResults::done(curJob); return false; } @@ -265,7 +268,18 @@ void xmrig::CpuWorker::start() if (valid) { for (size_t i = 0; i < N; ++i) { - if (*reinterpret_cast(m_hash + (i * 32) + 24) < job.target()) { + const uint64_t value = *reinterpret_cast(m_hash + (i * 32) + 24); + + if (job.bench()) { + if (current_job_nonces[i] < job.bench()) { + m_benchData ^= value; + } + else { + m_benchDoneTime = Chrono::steadyMSecs(); + return; + } + } + else if (value < job.target()) { JobResults::submit(job, current_job_nonces[i], m_hash + (i * 32)); } } @@ -362,7 +376,8 @@ void xmrig::CpuWorker::consumeJob() return; } - m_job.add(m_miner->job(), kReserveCount, Nonce::CPU); + const uint32_t bench = m_miner->job().bench(); + m_job.add(m_miner->job(), bench ? 1 : kReserveCount, Nonce::CPU); # ifdef XMRIG_ALGO_RANDOMX if (m_job.currentJob().algorithm().family() == Algorithm::RANDOM_X) { diff --git a/src/backend/cuda/CudaBackend.cpp b/src/backend/cuda/CudaBackend.cpp index 4d02ccbaa..ab5b22e53 100644 --- a/src/backend/cuda/CudaBackend.cpp +++ b/src/backend/cuda/CudaBackend.cpp @@ -501,9 +501,9 @@ void xmrig::CudaBackend::stop() } -void xmrig::CudaBackend::tick(uint64_t ticks) +bool xmrig::CudaBackend::tick(uint64_t ticks) { - d_ptr->workers.tick(ticks); + return d_ptr->workers.tick(ticks); } diff --git a/src/backend/cuda/CudaBackend.h b/src/backend/cuda/CudaBackend.h index 2e82c589a..ce684d5b3 100644 --- a/src/backend/cuda/CudaBackend.h +++ b/src/backend/cuda/CudaBackend.h @@ -63,7 +63,7 @@ protected: void setJob(const Job &job) override; void start(IWorker *worker, bool ready) override; void stop() override; - void tick(uint64_t ticks) override; + bool tick(uint64_t ticks) override; # ifdef XMRIG_FEATURE_API rapidjson::Value toJSON(rapidjson::Document &doc) const override; diff --git a/src/backend/opencl/OclBackend.cpp b/src/backend/opencl/OclBackend.cpp index 764df70f4..7b99700b5 100644 --- a/src/backend/opencl/OclBackend.cpp +++ b/src/backend/opencl/OclBackend.cpp @@ -485,9 +485,9 @@ void xmrig::OclBackend::stop() } -void xmrig::OclBackend::tick(uint64_t ticks) +bool xmrig::OclBackend::tick(uint64_t ticks) { - d_ptr->workers.tick(ticks); + return d_ptr->workers.tick(ticks); } diff --git a/src/backend/opencl/OclBackend.h b/src/backend/opencl/OclBackend.h index 0ed7b8eb0..49325d412 100644 --- a/src/backend/opencl/OclBackend.h +++ b/src/backend/opencl/OclBackend.h @@ -63,7 +63,7 @@ protected: void setJob(const Job &job) override; void start(IWorker *worker, bool ready) override; void stop() override; - void tick(uint64_t ticks) override; + bool tick(uint64_t ticks) override; # ifdef XMRIG_FEATURE_API rapidjson::Value toJSON(rapidjson::Document &doc) const override; diff --git a/src/base/base.cmake b/src/base/base.cmake index 0574d617e..6acf728de 100644 --- a/src/base/base.cmake +++ b/src/base/base.cmake @@ -44,6 +44,7 @@ set(HEADERS_BASE src/base/net/http/HttpListener.h src/base/net/stratum/BaseClient.h src/base/net/stratum/Client.h + src/base/net/stratum/NullClient.h src/base/net/stratum/Job.h src/base/net/stratum/NetworkState.h src/base/net/stratum/Pool.h @@ -97,6 +98,7 @@ set(SOURCES_BASE src/base/net/http/Http.cpp src/base/net/stratum/BaseClient.cpp src/base/net/stratum/Client.cpp + src/base/net/stratum/NullClient.cpp src/base/net/stratum/Job.cpp src/base/net/stratum/NetworkState.cpp src/base/net/stratum/Pool.cpp diff --git a/src/base/kernel/config/BaseTransform.cpp b/src/base/kernel/config/BaseTransform.cpp index 97041e7de..3694c73f3 100644 --- a/src/base/kernel/config/BaseTransform.cpp +++ b/src/base/kernel/config/BaseTransform.cpp @@ -152,6 +152,8 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch break; case IConfig::UrlKey: /* --url */ + case IConfig::StressKey: /* --stress */ + case IConfig::BenchKey: /* --bench */ { if (!doc.HasMember(Pools::kPools)) { doc.AddMember(rapidjson::StringRef(Pools::kPools), rapidjson::kArrayType, doc.GetAllocator()); @@ -162,7 +164,19 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch array.PushBack(rapidjson::kObjectType, doc.GetAllocator()); } - set(doc, array[array.Size() - 1], Pool::kUrl, arg); + if (key == IConfig::UrlKey) { + set(doc, array[array.Size() - 1], Pool::kUrl, arg); + } + else { + set(doc, array[array.Size() - 1], Pool::kUrl, (key == IConfig::BenchKey) ? "offline" : "donate.v2.xmrig.com:3333"); + set(doc, "cpu", "huge-pages-jit", true); + set(doc, "cpu", "priority", 2); + set(doc, "cpu", "yield", false); + if (key == IConfig::BenchKey) { + set(doc, array[array.Size() - 1], Pool::kBenchmark, arg); + } + } + break; } diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h index dd657c2d8..22f652271 100644 --- a/src/base/kernel/interfaces/IConfig.h +++ b/src/base/kernel/interfaces/IConfig.h @@ -77,6 +77,8 @@ public: TitleKey = 1037, NoTitleKey = 1038, PauseOnBatteryKey = 1041, + StressKey = 1042, + BenchKey = 1043, // xmrig common CPUPriorityKey = 1021, diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp index 1de1c19b1..d790fccf4 100644 --- a/src/base/net/stratum/Job.cpp +++ b/src/base/net/stratum/Job.cpp @@ -154,6 +154,7 @@ void xmrig::Job::copy(const Job &other) { m_algorithm = other.m_algorithm; m_nicehash = other.m_nicehash; + m_bench = other.m_bench; m_size = other.m_size; m_clientId = other.m_clientId; m_id = other.m_id; @@ -181,6 +182,7 @@ void xmrig::Job::move(Job &&other) { m_algorithm = other.m_algorithm; m_nicehash = other.m_nicehash; + m_bench = other.m_bench; m_size = other.m_size; m_clientId = std::move(other.m_clientId); m_id = std::move(other.m_id); diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h index ba5a0aa2e..31e377606 100644 --- a/src/base/net/stratum/Job.h +++ b/src/base/net/stratum/Job.h @@ -87,6 +87,7 @@ public: inline uint8_t *blob() { return m_blob; } inline uint8_t fixedByte() const { return *(m_blob + 42); } inline uint8_t index() const { return m_index; } + inline uint32_t bench() const { return m_bench; } inline void reset() { m_size = 0; m_diff = 0; } inline void setAlgorithm(const Algorithm::Id id) { m_algorithm = id; } inline void setAlgorithm(const char *algo) { m_algorithm = algo; } @@ -96,6 +97,7 @@ public: inline void setHeight(uint64_t height) { m_height = height; } inline void setIndex(uint8_t index) { m_index = index; } inline void setPoolWallet(const String &poolWallet) { m_poolWallet = poolWallet; } + inline void setBench(uint32_t bench) { m_bench = bench; } # ifdef XMRIG_PROXY_PROJECT inline char *rawBlob() { return m_rawBlob; } @@ -117,6 +119,7 @@ private: Algorithm m_algorithm; bool m_nicehash = false; + uint32_t m_bench = 0; Buffer m_seed; size_t m_size = 0; String m_clientId; diff --git a/src/base/net/stratum/NullClient.cpp b/src/base/net/stratum/NullClient.cpp new file mode 100644 index 000000000..a18638fe7 --- /dev/null +++ b/src/base/net/stratum/NullClient.cpp @@ -0,0 +1,63 @@ +/* XMRig + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "3rdparty/rapidjson/document.h" +#include "base/net/stratum/NullClient.h" +#include "base/kernel/interfaces/IClientListener.h" + + +xmrig::NullClient::NullClient(IClientListener* listener) : + m_listener(listener) +{ + m_job.setAlgorithm(Algorithm::RX_0); + + std::vector blob(112 * 2 + 1, '0'); + + blob.back() = '\0'; + m_job.setBlob(blob.data()); + + blob[Job::kMaxSeedSize * 2] = '\0'; + m_job.setSeedHash(blob.data()); + + m_job.setDiff(uint64_t(-1)); + m_job.setHeight(1); + + m_job.setId("00000000"); +} + + +void xmrig::NullClient::connect() +{ + m_listener->onLoginSuccess(this); + + rapidjson::Value params; + m_listener->onJobReceived(this, m_job, params); +} + + +void xmrig::NullClient::setPool(const Pool& pool) +{ + m_pool = pool; + + if (!m_pool.algorithm().isValid()) { + m_pool.setAlgo(Algorithm::RX_0); + } + + m_job.setAlgorithm(m_pool.algorithm().id()); + m_job.setBench(m_pool.benchSize()); +} diff --git a/src/base/net/stratum/NullClient.h b/src/base/net/stratum/NullClient.h new file mode 100644 index 000000000..e6b024ba6 --- /dev/null +++ b/src/base/net/stratum/NullClient.h @@ -0,0 +1,77 @@ +/* XMRig + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_NULLCLIENT_H +#define XMRIG_NULLCLIENT_H + + +#include "base/net/stratum/Client.h" + + +namespace xmrig { + + +class NullClient : public IClient +{ +public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(NullClient) + + NullClient(IClientListener* listener); + ~NullClient() override = default; + + virtual bool disconnect() override { return true; } + virtual bool hasExtension(Extension extension) const noexcept override { return false; } + virtual bool isEnabled() const override { return true; } + virtual bool isTLS() const override { return false; } + virtual const char* mode() const override { return "benchmark"; } + virtual const char* tag() const override { return "null"; } + virtual const char* tlsFingerprint() const override { return nullptr; } + virtual const char* tlsVersion() const override { return nullptr; } + virtual const Job& job() const override { return m_job; } + virtual const Pool& pool() const override { return m_pool; } + virtual const String& ip() const override { return m_ip; } + virtual int id() const override { return 0; } + virtual int64_t send(const rapidjson::Value& obj, Callback callback) override { return 0; } + virtual int64_t send(const rapidjson::Value& obj) override { return 0; } + virtual int64_t sequence() const override { return 0; } + virtual int64_t submit(const JobResult& result) override { return 0; } + virtual void connect() override; + virtual void connect(const Pool& pool) override { setPool(pool); } + virtual void deleteLater() override {} + virtual void setAlgo(const Algorithm& algo) override {} + virtual void setEnabled(bool enabled) override {} + virtual void setPool(const Pool& pool) override; + virtual void setProxy(const ProxyUrl& proxy) override {} + virtual void setQuiet(bool quiet) override {} + virtual void setRetries(int retries) override {} + virtual void setRetryPause(uint64_t ms) override {} + virtual void tick(uint64_t now) override {} + +private: + IClientListener* m_listener; + + Job m_job; + Pool m_pool; + String m_ip; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_NULLCLIENT_H */ diff --git a/src/base/net/stratum/Pool.cpp b/src/base/net/stratum/Pool.cpp index dc0ac7923..ba7b12bf4 100644 --- a/src/base/net/stratum/Pool.cpp +++ b/src/base/net/stratum/Pool.cpp @@ -50,6 +50,9 @@ #endif +#include "base/net/stratum/NullClient.h" + + namespace xmrig { @@ -72,6 +75,7 @@ const char *Pool::kSOCKS5 = "socks5"; const char *Pool::kTls = "tls"; const char *Pool::kUrl = "url"; const char *Pool::kUser = "user"; +const char *Pool::kBenchmark = "benchmark"; const char *Pool::kNicehashHost = "nicehash.com"; @@ -125,7 +129,20 @@ xmrig::Pool::Pool(const rapidjson::Value &object) : m_flags.set(FLAG_NICEHASH, Json::getBool(object, kNicehash) || m_url.host().contains(kNicehashHost)); m_flags.set(FLAG_TLS, Json::getBool(object, kTls) || m_url.isTLS()); - if (m_daemon.isValid()) { + const char* benchSize = Json::getString(object, kBenchmark, nullptr); + if (benchSize) { + if (stricmp(benchSize, "1M") == 0) { + m_benchSize = 1000000; + } + else if (stricmp(benchSize, "10M") == 0) { + m_benchSize = 10000000; + } + } + + if (m_benchSize) { + m_mode = MODE_BENCHMARK; + } + else if (m_daemon.isValid()) { m_mode = MODE_SELF_SELECT; } else if (Json::getBool(object, kDaemon)) { @@ -217,6 +234,9 @@ xmrig::IClient *xmrig::Pool::createClient(int id, IClientListener *listener) con client = new AutoClient(id, Platform::userAgent(), listener); } # endif + else if (m_mode == MODE_BENCHMARK) { + client = new NullClient(listener); + } assert(client != nullptr); diff --git a/src/base/net/stratum/Pool.h b/src/base/net/stratum/Pool.h index 7569eae79..dd4158a93 100644 --- a/src/base/net/stratum/Pool.h +++ b/src/base/net/stratum/Pool.h @@ -50,7 +50,8 @@ public: MODE_POOL, MODE_DAEMON, MODE_SELF_SELECT, - MODE_AUTO_ETH + MODE_AUTO_ETH, + MODE_BENCHMARK, }; static const String kDefaultPassword; @@ -71,6 +72,7 @@ public: static const char *kTls; static const char *kUrl; static const char *kUser; + static const char* kBenchmark; static const char *kNicehashHost; constexpr static int kKeepAliveTimeout = 60; @@ -97,6 +99,7 @@ public: inline const Url &daemon() const { return m_daemon; } inline int keepAlive() const { return m_keepAlive; } inline Mode mode() const { return m_mode; } + inline uint64_t benchSize() const { return m_benchSize; } inline uint16_t port() const { return m_url.port(); } inline uint64_t pollInterval() const { return m_pollInterval; } inline void setAlgo(const Algorithm &algorithm) { m_algorithm = algorithm; } @@ -133,6 +136,7 @@ private: Coin m_coin; int m_keepAlive = 0; Mode m_mode = MODE_POOL; + uint32_t m_benchSize = 0; ProxyUrl m_proxy; std::bitset m_flags = 0; String m_fingerprint; diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index 5dab2ebbb..b3032cf14 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -573,8 +573,12 @@ void xmrig::Miner::onTimer(const Timer *) double maxHashrate = 0.0; const auto healthPrintTime = d_ptr->controller->config()->healthPrintTime(); + bool stopMiner = false; + for (IBackend *backend : d_ptr->backends) { - backend->tick(d_ptr->ticks); + if (!backend->tick(d_ptr->ticks)) { + stopMiner = true; + } if (healthPrintTime && d_ptr->ticks && (d_ptr->ticks % (healthPrintTime * 2)) == 0 && backend->isEnabled()) { backend->printHealth(); @@ -607,6 +611,10 @@ void xmrig::Miner::onTimer(const Timer *) setEnabled(true); } } + + if (stopMiner) { + stop(); + } } diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h index 3c6329f5b..817da6fe8 100644 --- a/src/core/config/Config_platform.h +++ b/src/core/config/Config_platform.h @@ -96,6 +96,8 @@ static const option options[] = { { "title", 1, nullptr, IConfig::TitleKey }, { "no-title", 0, nullptr, IConfig::NoTitleKey }, { "pause-on-battery", 0, nullptr, IConfig::PauseOnBatteryKey }, + { "stress", 0, nullptr, IConfig::StressKey }, + { "bench", 1, nullptr, IConfig::BenchKey }, # ifdef XMRIG_FEATURE_TLS { "tls", 0, nullptr, IConfig::TlsKey }, { "tls-fingerprint", 1, nullptr, IConfig::FingerprintKey }, diff --git a/src/core/config/usage.h b/src/core/config/usage.h index 1b68973af..15393f9c9 100644 --- a/src/core/config/usage.h +++ b/src/core/config/usage.h @@ -178,6 +178,8 @@ static inline const std::string &usage() u += " --no-title disable setting console window title\n"; # endif u += " --pause-on-battery pause mine on battery power\n"; + u += " --stress run continuous stress test to check system stability\n"; + u += " --bench=N run benchmark in offline mode, N can be 1M or 10M\n"; return u; } diff --git a/src/net/Network.cpp b/src/net/Network.cpp index 0bb741d44..18cec549e 100644 --- a/src/net/Network.cpp +++ b/src/net/Network.cpp @@ -74,7 +74,10 @@ xmrig::Network::Network(Controller *controller) : m_strategy = pools.createStrategy(m_state); if (pools.donateLevel() > 0) { - m_donate = new DonateStrategy(controller, this); + const bool bench = (pools.data().size() == 1) && (pools.data().front().mode() == xmrig::Pool::MODE_BENCHMARK); + if (!bench) { + m_donate = new DonateStrategy(controller, this); + } } m_timer = new Timer(this, kTickInterval, kTickInterval);