From 6d8cf91568202c9c6f5313950afce34665e4ca7e Mon Sep 17 00:00:00 2001 From: XMRig Date: Thu, 3 Oct 2019 04:48:36 +0700 Subject: [PATCH 01/11] Added class RxSeed. --- cmake/randomx.cmake | 1 + src/crypto/rx/Rx.cpp | 17 +++++------ src/crypto/rx/RxSeed.h | 69 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 src/crypto/rx/RxSeed.h diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake index 27b6e5e64..ac0acde9b 100644 --- a/cmake/randomx.cmake +++ b/cmake/randomx.cmake @@ -7,6 +7,7 @@ if (WITH_RANDOMX) src/crypto/rx/RxCache.h src/crypto/rx/RxConfig.h src/crypto/rx/RxDataset.h + src/crypto/rx/RxSeed.h src/crypto/rx/RxVm.h ) diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index d0674b299..4382e10d7 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -39,6 +39,7 @@ #include "crypto/rx/RxAlgo.h" #include "crypto/rx/RxCache.h" #include "crypto/rx/RxDataset.h" +#include "crypto/rx/RxSeed.h" #ifdef XMRIG_FEATURE_HWLOC @@ -133,9 +134,8 @@ public: inline bool isNUMA() const { return m_numa; } - inline bool isReady(const Job &job) const { return m_ready == count() && m_algorithm == job.algorithm() && m_seed == job.seed(); } - inline const Algorithm &algorithm() const { return m_algorithm; } - inline const Buffer &seed() const { return m_seed; } + inline bool isReady(const Job &job) const { return m_ready == count() && m_seed == job; } + inline const Algorithm &algorithm() const { return m_seed.algorithm(); } inline size_t count() const { return isNUMA() ? datasets.size() : 1; } inline uint64_t counter() { return m_counter.load(std::memory_order_relaxed); } inline void asyncSend(uint64_t counter) { m_ready++; if (m_ready == count()) { m_last = counter; uv_async_send(m_async); } } @@ -186,7 +186,7 @@ public: const uint64_t ts = Chrono::steadyMSecs(); - d_ptr->getOrAllocate(nodeId)->init(d_ptr->seed(), threads); + d_ptr->getOrAllocate(nodeId)->init(d_ptr->m_seed.data(), threads); d_ptr->asyncSend(counter); LOG_INFO("%s" CYAN_BOLD("#%u") GREEN(" init done ") CYAN_BOLD("%zu/%zu") BLACK_BOLD(" (%" PRIu64 " ms)"), tag, nodeId, d_ptr->m_ready, d_ptr->count(), Chrono::steadyMSecs() - ts); @@ -217,15 +217,15 @@ public: inline void setState(const Job &job, bool hugePages, bool numa, IRxListener *listener) { - if (m_algorithm != job.algorithm()) { - m_algorithm = RxAlgo::apply(job.algorithm()); + if (m_seed.algorithm() != job.algorithm()) { + RxAlgo::apply(job.algorithm()); } m_ready = 0; m_numa = numa && Cpu::info()->nodes() > 1; m_hugePages = hugePages; m_listener = listener; - m_seed = job.seed(); + m_seed = job; ++m_counter; } @@ -242,10 +242,9 @@ private: } - Algorithm m_algorithm; bool m_hugePages = true; bool m_numa = true; - Buffer m_seed; + RxSeed m_seed; IRxListener *m_listener = nullptr; size_t m_ready = 0; std::atomic m_counter; diff --git a/src/crypto/rx/RxSeed.h b/src/crypto/rx/RxSeed.h new file mode 100644 index 000000000..c8993a18f --- /dev/null +++ b/src/crypto/rx/RxSeed.h @@ -0,0 +1,69 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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_RX_SEED_H +#define XMRIG_RX_SEED_H + + +#include "base/net/stratum/Job.h" +#include "base/tools/Buffer.h" + + +namespace xmrig +{ + + +class RxSeed; + + +class RxSeed +{ +public: + RxSeed() = default; + + inline RxSeed(const Algorithm &algorithm, const Buffer &seed) : m_algorithm(algorithm), m_data(seed) {} + inline RxSeed(const Job &job) : m_algorithm(job.algorithm()), m_data(job.seed()) {} + + inline bool isEqual(const Job &job) const { return m_algorithm == job.algorithm() && m_data == job.seed(); } + inline bool isEqual(const RxSeed &other) const { return m_algorithm == other.m_algorithm && m_data == other.m_data; } + inline const Algorithm &algorithm() const { return m_algorithm; } + inline const Buffer &data() const { return m_data; } + + inline bool operator!=(const Job &job) const { return !isEqual(job); } + inline bool operator!=(const RxSeed &other) const { return !isEqual(other); } + inline bool operator==(const Job &job) const { return isEqual(job); } + inline bool operator==(const RxSeed &other) const { return isEqual(other); } + +private: + Algorithm m_algorithm; + Buffer m_data; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_CACHE_H */ From d1aadc2e3b7b52135a5b75b3214dd7697c6456df Mon Sep 17 00:00:00 2001 From: XMRig Date: Thu, 3 Oct 2019 07:45:25 +0700 Subject: [PATCH 02/11] More cleanup. --- src/core/Miner.cpp | 2 +- src/core/Miner.h | 3 +++ src/core/config/Config.cpp | 6 +++--- src/crypto/rx/Rx.cpp | 9 +++++---- src/crypto/rx/Rx.h | 3 ++- src/crypto/rx/RxConfig.cpp | 17 ++++++++++++++++- src/crypto/rx/RxConfig.h | 9 +++++++-- 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index b6b899487..cbba3de34 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -234,7 +234,7 @@ public: # ifdef XMRIG_ALGO_RANDOMX bool initRX(IRxListener *listener) { - return Rx::init(job, controller->config()->rx().threads(), controller->config()->cpu().isHugePages(), controller->config()->rx().isNUMA(), listener); + return Rx::init(job, controller->config()->rx(), controller->config()->cpu().isHugePages(), listener); } # endif diff --git a/src/core/Miner.h b/src/core/Miner.h index 21568f122..6f4149c30 100644 --- a/src/core/Miner.h +++ b/src/core/Miner.h @@ -33,6 +33,7 @@ #include "base/api/interfaces/IApiListener.h" #include "base/kernel/interfaces/IBaseListener.h" #include "base/kernel/interfaces/ITimerListener.h" +#include "base/tools/Object.h" #include "crypto/common/Algorithm.h" @@ -48,6 +49,8 @@ class IBackend; class Miner : public ITimerListener, public IBaseListener, public IApiListener, public IRxListener { public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(Miner) + Miner(Controller *controller); ~Miner() override; diff --git a/src/core/config/Config.cpp b/src/core/config/Config.cpp index 33e5e54fe..17b0e1669 100644 --- a/src/core/config/Config.cpp +++ b/src/core/config/Config.cpp @@ -23,9 +23,9 @@ */ #include -#include +#include #include -#include +#include #include "backend/cpu/Cpu.h" @@ -79,7 +79,7 @@ public: } -xmrig::Config::Config() : BaseConfig(), +xmrig::Config::Config() : d_ptr(new ConfigPrivate()) { } diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 4382e10d7..25b633ec9 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -38,6 +38,7 @@ #include "base/tools/Object.h" #include "crypto/rx/RxAlgo.h" #include "crypto/rx/RxCache.h" +#include "crypto/rx/RxConfig.h" #include "crypto/rx/RxDataset.h" #include "crypto/rx/RxSeed.h" @@ -222,7 +223,7 @@ public: } m_ready = 0; - m_numa = numa && Cpu::info()->nodes() > 1; + m_numa = numa; m_hugePages = hugePages; m_listener = listener; m_seed = job; @@ -256,7 +257,7 @@ private: } // namespace xmrig -bool xmrig::Rx::init(const Job &job, int initThreads, bool hugePages, bool numa, IRxListener *listener) +bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, IRxListener *listener) { if (job.algorithm().family() != Algorithm::RANDOM_X) { return true; @@ -268,8 +269,8 @@ bool xmrig::Rx::init(const Job &job, int initThreads, bool hugePages, bool numa, return true; } - d_ptr->setState(job, hugePages, numa, listener); - const uint32_t threads = initThreads < 1 ? static_cast(Cpu::info()->threads()) : static_cast(initThreads); + d_ptr->setState(job, hugePages, config.isNUMA(), listener); + const uint32_t threads = config.threads(); const String buf = Buffer::toHex(job.seed().data(), 8); const uint64_t counter = d_ptr->counter(); diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index 9392e80db..33eddc189 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -39,13 +39,14 @@ namespace xmrig class Algorithm; class IRxListener; class Job; +class RxConfig; class RxDataset; class Rx { public: - static bool init(const Job &job, int initThreads, bool hugePages, bool numa, IRxListener *listener); + static bool init(const Job &job, const RxConfig &config, bool hugePages, IRxListener *listener); static bool isReady(const Job &job); static RxDataset *dataset(const Job &job, uint32_t nodeId); static std::pair hugePages(); diff --git a/src/crypto/rx/RxConfig.cpp b/src/crypto/rx/RxConfig.cpp index dc543fb43..63f60b3bf 100644 --- a/src/crypto/rx/RxConfig.cpp +++ b/src/crypto/rx/RxConfig.cpp @@ -23,8 +23,9 @@ */ -#include "base/io/json/Json.h" #include "crypto/rx/RxConfig.h" +#include "backend/cpu/Cpu.h" +#include "base/io/json/Json.h" #include "rapidjson/document.h" @@ -61,3 +62,17 @@ bool xmrig::RxConfig::read(const rapidjson::Value &value) return false; } + + +#ifdef XMRIG_FEATURE_HWLOC +bool xmrig::RxConfig::isNUMA() const +{ + return m_numa && Cpu::info()->nodes() > 1; +} +#endif + + +uint32_t xmrig::RxConfig::threads() const +{ + return m_threads < 1 ? static_cast(Cpu::info()->threads()) : static_cast(m_threads); +} diff --git a/src/crypto/rx/RxConfig.h b/src/crypto/rx/RxConfig.h index e06c764cf..343a884b0 100644 --- a/src/crypto/rx/RxConfig.h +++ b/src/crypto/rx/RxConfig.h @@ -38,8 +38,13 @@ public: bool read(const rapidjson::Value &value); rapidjson::Value toJSON(rapidjson::Document &doc) const; - inline bool isNUMA() const { return m_numa; } - inline int threads() const { return m_threads; } +# ifdef XMRIG_FEATURE_HWLOC + bool isNUMA() const; +# else + inline constexpr bool isNUMA() const { return false; } +# endif + + uint32_t threads() const; private: bool m_numa = true; From f34031a9849668560df364adfc3add48d5348dc6 Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 4 Oct 2019 08:45:13 +0700 Subject: [PATCH 03/11] Added interface IRxStorage and RxBasicStorage class. --- cmake/randomx.cmake | 2 + src/backend/common/Tags.h | 5 + src/backend/common/common.cmake | 1 + src/backend/common/interfaces/IRxStorage.h | 53 +++++ src/crypto/rx/Rx.cpp | 229 ++++----------------- src/crypto/rx/Rx.h | 2 +- src/crypto/rx/RxBasicStorage.cpp | 160 ++++++++++++++ src/crypto/rx/RxBasicStorage.h | 63 ++++++ src/crypto/rx/RxDataset.cpp | 61 ++++-- src/crypto/rx/RxDataset.h | 6 +- 10 files changed, 372 insertions(+), 210 deletions(-) create mode 100644 src/backend/common/interfaces/IRxStorage.h create mode 100644 src/crypto/rx/RxBasicStorage.cpp create mode 100644 src/crypto/rx/RxBasicStorage.h diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake index ac0acde9b..6c150d491 100644 --- a/cmake/randomx.cmake +++ b/cmake/randomx.cmake @@ -4,6 +4,7 @@ if (WITH_RANDOMX) list(APPEND HEADERS_CRYPTO src/crypto/rx/Rx.h src/crypto/rx/RxAlgo.h + src/crypto/rx/RxBasicStorage.h src/crypto/rx/RxCache.h src/crypto/rx/RxConfig.h src/crypto/rx/RxDataset.h @@ -33,6 +34,7 @@ if (WITH_RANDOMX) src/crypto/randomx/vm_interpreted.cpp src/crypto/rx/Rx.cpp src/crypto/rx/RxAlgo.cpp + src/crypto/rx/RxBasicStorage.cpp src/crypto/rx/RxCache.cpp src/crypto/rx/RxConfig.cpp src/crypto/rx/RxDataset.cpp diff --git a/src/backend/common/Tags.h b/src/backend/common/Tags.h index 938a1bc49..16022e33e 100644 --- a/src/backend/common/Tags.h +++ b/src/backend/common/Tags.h @@ -38,6 +38,11 @@ const char *ocl_tag(); #endif +#ifdef XMRIG_ALGO_RANDOMX +const char *rx_tag(); +#endif + + } // namespace xmrig diff --git a/src/backend/common/common.cmake b/src/backend/common/common.cmake index cddd40014..9dd0fb3c1 100644 --- a/src/backend/common/common.cmake +++ b/src/backend/common/common.cmake @@ -3,6 +3,7 @@ set(HEADERS_BACKEND_COMMON src/backend/common/Tags.h src/backend/common/interfaces/IBackend.h src/backend/common/interfaces/IRxListener.h + src/backend/common/interfaces/IRxStorage.h src/backend/common/interfaces/IThread.h src/backend/common/interfaces/IWorker.h src/backend/common/misc/PciTopology.h diff --git a/src/backend/common/interfaces/IRxStorage.h b/src/backend/common/interfaces/IRxStorage.h new file mode 100644 index 000000000..49273ee3d --- /dev/null +++ b/src/backend/common/interfaces/IRxStorage.h @@ -0,0 +1,53 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2018 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_IRXSTORAGE_H +#define XMRIG_IRXSTORAGE_H + + +#include +#include + + +namespace xmrig { + + +class Job; +class RxDataset; +class RxSeed; + + +class IRxStorage +{ +public: + virtual ~IRxStorage() = default; + + virtual RxDataset *dataset(const Job &job, uint32_t nodeId) const = 0; + virtual std::pair hugePages() const = 0; + virtual void init(const RxSeed &seed, uint32_t threads, bool hugePages) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IRXSTORAGE_H diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 25b633ec9..f688a9dd7 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -28,6 +28,8 @@ #include "crypto/rx/Rx.h" #include "backend/common/interfaces/IRxListener.h" +#include "backend/common/interfaces/IRxStorage.h" +#include "backend/common/Tags.h" #include "backend/cpu/Cpu.h" #include "base/io/log/Log.h" #include "base/kernel/Platform.h" @@ -37,18 +39,13 @@ #include "base/tools/Handle.h" #include "base/tools/Object.h" #include "crypto/rx/RxAlgo.h" +#include "crypto/rx/RxBasicStorage.h" #include "crypto/rx/RxCache.h" #include "crypto/rx/RxConfig.h" #include "crypto/rx/RxDataset.h" #include "crypto/rx/RxSeed.h" -#ifdef XMRIG_FEATURE_HWLOC -# include -# include "backend/cpu/platform/HwlocCpuInfo.h" -#endif - - #include #include #include @@ -67,58 +64,18 @@ static RxPrivate *d_ptr = nullptr; static std::mutex mutex; -#ifdef XMRIG_FEATURE_HWLOC -static void bindToNUMANode(uint32_t nodeId) -{ - hwloc_topology_t topology; - hwloc_topology_init(&topology); - hwloc_topology_load(topology); - - hwloc_obj_t node = hwloc_get_numanode_obj_by_os_index(topology, nodeId); - if (node) { - if (HwlocCpuInfo::has(HwlocCpuInfo::SET_THISTHREAD_MEMBIND)) { -# if HWLOC_API_VERSION >= 0x20000 - hwloc_set_membind(topology, node->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET); -# else - hwloc_set_membind_nodeset(topology, node->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD); -# endif - } - - Platform::setThreadAffinity(static_cast(hwloc_bitmap_first(node->cpuset))); - } - - hwloc_topology_destroy(topology); -} -#else -inline static void bindToNUMANode(uint32_t) {} -#endif - - class RxPrivate { public: XMRIG_DISABLE_COPY_MOVE(RxPrivate) inline RxPrivate() : - m_counter(0), - m_last(0) + m_pending(0) { m_async = new uv_async_t; m_async->data = this; - uv_async_init(uv_default_loop(), m_async, [](uv_async_t *) { d_ptr->onReady(); }); - -# ifdef XMRIG_FEATURE_HWLOC - if (Cpu::info()->nodes() > 1) { - for (uint32_t nodeId : HwlocCpuInfo::nodeIndexes()) { - datasets.insert({ nodeId, nullptr }); - } - } - else -# endif - { - datasets.insert({ 0, nullptr }); - } + uv_async_init(uv_default_loop(), m_async, [](uv_async_t *handle) { static_cast(handle->data)->onReady(); }); } @@ -126,130 +83,66 @@ public: { Handle::close(m_async); - for (auto const &item : datasets) { - delete item.second; - } - - datasets.clear(); + delete m_storage; } - inline bool isNUMA() const { return m_numa; } - inline bool isReady(const Job &job) const { return m_ready == count() && m_seed == job; } - inline const Algorithm &algorithm() const { return m_seed.algorithm(); } - inline size_t count() const { return isNUMA() ? datasets.size() : 1; } - inline uint64_t counter() { return m_counter.load(std::memory_order_relaxed); } - inline void asyncSend(uint64_t counter) { m_ready++; if (m_ready == count()) { m_last = counter; uv_async_send(m_async); } } + inline bool isReady(const Job &job) const { return pending() == 0 && m_seed == job; } + inline RxDataset *dataset(const Job &job, uint32_t nodeId) { return m_storage ? m_storage->dataset(job, nodeId) : nullptr; } + inline std::pair hugePages() { return m_storage ? m_storage->hugePages() : std::pair(0u, 0u); } + inline uint64_t pending() const { return m_pending.load(std::memory_order_relaxed); } + inline void asyncSend() { --m_pending; if (pending() == 0) { uv_async_send(m_async); } } - static void allocate(uint32_t nodeId) + + inline IRxStorage *storage() { - const uint64_t ts = Chrono::steadyMSecs(); - - if (d_ptr->isNUMA()) { - bindToNUMANode(nodeId); + if (!m_storage) { + m_storage = new RxBasicStorage(); } - LOG_INFO("%s" CYAN_BOLD("#%u") MAGENTA_BOLD(" allocate") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu) for RandomX dataset & cache"), - tag, - nodeId, - (RxDataset::maxSize() + RxCache::maxSize()) / 1024 / 1024, - RxDataset::maxSize() / 1024 / 1024, - RxCache::maxSize() / 1024 / 1024 - ); - - auto dataset = new RxDataset(d_ptr->m_hugePages); - d_ptr->datasets[nodeId] = dataset; - - if (dataset->get() != nullptr) { - const auto hugePages = dataset->hugePages(); - const double percent = hugePages.first == 0 ? 0.0 : static_cast(hugePages.first) / hugePages.second * 100.0; - - LOG_INFO("%s" CYAN_BOLD("#%u") GREEN(" allocate done") " huge pages %s%u/%u %1.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), - tag, - nodeId, - (hugePages.first == hugePages.second ? GREEN_BOLD_S : (hugePages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), - hugePages.first, - hugePages.second, - percent, - dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", - Chrono::steadyMSecs() - ts - ); - } - else { - LOG_WARN(CLEAR "%s" CYAN_BOLD("#%u") YELLOW_BOLD_S " failed to allocate RandomX dataset, switching to slow mode", tag, nodeId); - } + return m_storage; } - static void initDataset(uint32_t nodeId, uint32_t threads, uint64_t counter) + static void initDataset(const RxSeed &seed, uint32_t threads, bool hugePages) { std::lock_guard lock(mutex); - const uint64_t ts = Chrono::steadyMSecs(); + LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."), + tag, + false ? "s" : "", // FIXME + seed.algorithm().shortName(), + threads, + Buffer::toHex(seed.data().data(), 8).data() + ); - d_ptr->getOrAllocate(nodeId)->init(d_ptr->m_seed.data(), threads); - d_ptr->asyncSend(counter); - - LOG_INFO("%s" CYAN_BOLD("#%u") GREEN(" init done ") CYAN_BOLD("%zu/%zu") BLACK_BOLD(" (%" PRIu64 " ms)"), tag, nodeId, d_ptr->m_ready, d_ptr->count(), Chrono::steadyMSecs() - ts); + d_ptr->storage()->init(seed, threads, hugePages); + d_ptr->asyncSend(); } - inline RxDataset *getOrAllocate(uint32_t nodeId) + inline void setState(const Job &job, IRxListener *listener) { - RxDataset *dataset = datasets.at(nodeId); - - if (dataset == nullptr) { - # ifdef XMRIG_FEATURE_HWLOC - if (d_ptr->isNUMA()) { - std::thread thread(allocate, nodeId); - thread.join(); - } else - # endif - { - allocate(nodeId); - } - - dataset = datasets.at(nodeId); - } - - return dataset; - } - - - inline void setState(const Job &job, bool hugePages, bool numa, IRxListener *listener) - { - if (m_seed.algorithm() != job.algorithm()) { - RxAlgo::apply(job.algorithm()); - } - - m_ready = 0; - m_numa = numa; - m_hugePages = hugePages; m_listener = listener; m_seed = job; - ++m_counter; + ++m_pending; } - std::map datasets; - private: inline void onReady() { - if (m_listener && counter() == m_last.load(std::memory_order_relaxed)) { + if (m_listener && pending() == 0) { m_listener->onDatasetReady(); } } - bool m_hugePages = true; - bool m_numa = true; - RxSeed m_seed; IRxListener *m_listener = nullptr; - size_t m_ready = 0; - std::atomic m_counter; - std::atomic m_last; + IRxStorage *m_storage = nullptr; + RxSeed m_seed; + std::atomic m_pending; uv_async_t *m_async = nullptr; }; @@ -257,44 +150,26 @@ private: } // namespace xmrig +const char *xmrig::rx_tag() +{ + return tag; +} + + bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, IRxListener *listener) { if (job.algorithm().family() != Algorithm::RANDOM_X) { return true; } - std::lock_guard lock(mutex); - if (d_ptr->isReady(job)) { return true; } - d_ptr->setState(job, hugePages, config.isNUMA(), listener); - const uint32_t threads = config.threads(); - const String buf = Buffer::toHex(job.seed().data(), 8); - const uint64_t counter = d_ptr->counter(); + d_ptr->setState(job, listener); - LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."), - tag, - d_ptr->count() > 1 ? "s" : "", - job.algorithm().shortName(), - threads, - buf.data() - ); - -# ifdef XMRIG_FEATURE_HWLOC - if (d_ptr->isNUMA()) { - for (auto const &item : d_ptr->datasets) { - std::thread thread(RxPrivate::initDataset, item.first, threads, counter); - thread.detach(); - } - } - else -# endif - { - std::thread thread(RxPrivate::initDataset, 0, threads, counter); - thread.detach(); - } + std::thread thread(RxPrivate::initDataset, job, config.threads(), hugePages); + thread.detach(); return false; } @@ -302,8 +177,6 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, IRx bool xmrig::Rx::isReady(const Job &job) { - std::lock_guard lock(mutex); - return d_ptr->isReady(job); } @@ -311,30 +184,16 @@ bool xmrig::Rx::isReady(const Job &job) xmrig::RxDataset *xmrig::Rx::dataset(const Job &job, uint32_t nodeId) { std::lock_guard lock(mutex); - if (!d_ptr->isReady(job)) { - return nullptr; - } - return d_ptr->datasets.at(d_ptr->isNUMA() ? (d_ptr->datasets.count(nodeId) ? nodeId : HwlocCpuInfo::nodeIndexes().front()) : 0); + return d_ptr->dataset(job, nodeId); } -std::pair xmrig::Rx::hugePages() +std::pair xmrig::Rx::hugePages() { - std::pair pages(0, 0); std::lock_guard lock(mutex); - for (auto const &item : d_ptr->datasets) { - if (!item.second) { - continue; - } - - const auto p = item.second->hugePages(); - pages.first += p.first; - pages.second += p.second; - } - - return pages; + return d_ptr->hugePages(); } diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index 33eddc189..df3194137 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -49,7 +49,7 @@ public: static bool init(const Job &job, const RxConfig &config, bool hugePages, IRxListener *listener); static bool isReady(const Job &job); static RxDataset *dataset(const Job &job, uint32_t nodeId); - static std::pair hugePages(); + static std::pair hugePages(); static void destroy(); static void init(); }; diff --git a/src/crypto/rx/RxBasicStorage.cpp b/src/crypto/rx/RxBasicStorage.cpp new file mode 100644 index 000000000..6b1509cc8 --- /dev/null +++ b/src/crypto/rx/RxBasicStorage.cpp @@ -0,0 +1,160 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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 "crypto/rx/RxBasicStorage.h" +#include "backend/common/Tags.h" +#include "base/io/log/Log.h" +#include "base/tools/Chrono.h" +#include "crypto/rx/RxAlgo.h" +#include "crypto/rx/RxCache.h" +#include "crypto/rx/RxDataset.h" +#include "crypto/rx/RxSeed.h" + + +namespace xmrig { + + +constexpr size_t oneMiB = 1024 * 1024; + + +class RxBasicStoragePrivate +{ +public: + inline bool isReady(const Job &job) const { return m_ready && m_seed == job; } + inline RxDataset *dataset() const { return m_dataset; } + + + inline void setSeed(const RxSeed &seed) + { + m_ready = false; + + if (m_seed.algorithm() != seed.algorithm()) { + RxAlgo::apply(seed.algorithm()); + } + + m_seed = seed; + } + + + inline void createDataset(bool hugePages) + { + const uint64_t ts = Chrono::steadyMSecs(); + + m_dataset = new RxDataset(hugePages, true); + printAllocStatus(ts); + } + + + inline void initDataset(uint32_t threads, uint64_t ts) + { + m_dataset->init(m_seed.data(), threads); + + LOG_INFO("%s" GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); + + m_ready = true; + } + + +private: + void printAllocStatus(uint64_t ts) + { + if (m_dataset->get() != nullptr) { + const auto pages = m_dataset->hugePages(); + const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + + LOG_INFO("%s" GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu)") " huge pages %s%u/%u %1.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), + rx_tag(), + (RxDataset::maxSize() + RxCache::maxSize()) / oneMiB, + RxDataset::maxSize() / oneMiB, + RxCache::maxSize() / oneMiB, + (pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + pages.first, + pages.second, + percent, + m_dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", + Chrono::steadyMSecs() - ts + ); + } + else { + LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "failed to allocate RandomX dataset, switching to slow mode" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); + } + } + + + bool m_ready = false; + RxDataset *m_dataset = nullptr; + RxSeed m_seed; +}; + + +} // namespace xmrig + + +xmrig::RxBasicStorage::RxBasicStorage() : + d_ptr(new RxBasicStoragePrivate()) +{ +} + + +xmrig::RxBasicStorage::~RxBasicStorage() +{ + delete d_ptr; +} + + +xmrig::RxDataset *xmrig::RxBasicStorage::dataset(const Job &job, uint32_t) const +{ + if (!d_ptr->isReady(job)) { + return nullptr; + } + + return d_ptr->dataset(); +} + + +std::pair xmrig::RxBasicStorage::hugePages() const +{ + if (!d_ptr->dataset()) { + return { 0u, 0u }; + } + + return d_ptr->dataset()->hugePages(); +} + + +void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages) +{ + const uint64_t ts = Chrono::steadyMSecs(); + + d_ptr->setSeed(seed); + + if (!d_ptr->dataset()) { + d_ptr->createDataset(hugePages); + } + + d_ptr->initDataset(threads, ts); +} diff --git a/src/crypto/rx/RxBasicStorage.h b/src/crypto/rx/RxBasicStorage.h new file mode 100644 index 000000000..d6dad10dc --- /dev/null +++ b/src/crypto/rx/RxBasicStorage.h @@ -0,0 +1,63 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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_RX_VM_H +#define XMRIG_RX_VM_H + + +#include "backend/common/interfaces/IRxStorage.h" +#include "base/tools/Object.h" + + +namespace xmrig +{ + + +class RxBasicStoragePrivate; + + +class RxBasicStorage : public IRxStorage +{ +public: + XMRIG_DISABLE_COPY_MOVE(RxBasicStorage); + + RxBasicStorage(); + ~RxBasicStorage() override; + +protected: + RxDataset *dataset(const Job &job, uint32_t nodeId) const override; + std::pair hugePages() const override; + void init(const RxSeed &seed, uint32_t threads, bool hugePages) override; + +private: + RxBasicStoragePrivate *d_ptr; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_VM_H */ diff --git a/src/crypto/rx/RxDataset.cpp b/src/crypto/rx/RxDataset.cpp index 7384a9825..f5572c2a8 100644 --- a/src/crypto/rx/RxDataset.cpp +++ b/src/crypto/rx/RxDataset.cpp @@ -25,32 +25,26 @@ */ -#include - - +#include "crypto/rx/RxDataset.h" #include "crypto/common/VirtualMemory.h" #include "crypto/randomx/randomx.h" #include "crypto/rx/RxAlgo.h" #include "crypto/rx/RxCache.h" -#include "crypto/rx/RxDataset.h" + + +#include static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mismatch"); -xmrig::RxDataset::RxDataset(bool hugePages) +xmrig::RxDataset::RxDataset(bool hugePages, bool cache) { - if (hugePages) { - m_flags = RANDOMX_FLAG_LARGE_PAGES; - m_dataset = randomx_alloc_dataset(static_cast(m_flags)); - } + allocate(hugePages); - if (!m_dataset) { - m_flags = RANDOMX_FLAG_DEFAULT; - m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + if (cache) { + m_cache = new RxCache(hugePages); } - - m_cache = new RxCache(hugePages); } @@ -66,7 +60,11 @@ xmrig::RxDataset::~RxDataset() bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads) { - cache()->init(seed); + if (!m_cache) { + return false; + } + + m_cache->init(seed); if (!get()) { return true; @@ -96,18 +94,23 @@ bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads) } -std::pair xmrig::RxDataset::hugePages() const +std::pair xmrig::RxDataset::hugePages(bool cache) const { - constexpr size_t twoMiB = 2u * 1024u * 1024u; - constexpr const size_t total = (VirtualMemory::align(maxSize(), twoMiB) + VirtualMemory::align(RxCache::maxSize(), twoMiB)) / twoMiB; + constexpr size_t twoMiB = 2u * 1024u * 1024u; + constexpr size_t cacheSize = VirtualMemory::align(RxCache::maxSize(), twoMiB) / twoMiB; + size_t total = VirtualMemory::align(maxSize(), twoMiB) / twoMiB; - size_t count = 0; + uint32_t count = 0; if (isHugePages()) { - count += VirtualMemory::align(maxSize(), twoMiB) / twoMiB; + count += total; } - if (m_cache->isHugePages()) { - count += VirtualMemory::align(RxCache::maxSize(), twoMiB) / twoMiB; + if (cache && m_cache) { + total += cacheSize; + + if (m_cache->isHugePages()) { + count += cacheSize; + } } return { count, total }; @@ -118,3 +121,17 @@ void *xmrig::RxDataset::raw() const { return m_dataset ? randomx_get_dataset_memory(m_dataset) : nullptr; } + + +void xmrig::RxDataset::allocate(bool hugePages) +{ + if (hugePages) { + m_flags = RANDOMX_FLAG_LARGE_PAGES; + m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + } + + if (!m_dataset) { + m_flags = RANDOMX_FLAG_DEFAULT; + m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + } +} diff --git a/src/crypto/rx/RxDataset.h b/src/crypto/rx/RxDataset.h index e1b359a40..5ca10e004 100644 --- a/src/crypto/rx/RxDataset.h +++ b/src/crypto/rx/RxDataset.h @@ -49,7 +49,7 @@ class RxDataset public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxDataset) - RxDataset(bool hugePages = true); + RxDataset(bool hugePages, bool cache); ~RxDataset(); inline bool isHugePages() const { return m_flags & 1; } @@ -58,12 +58,14 @@ public: inline size_t size() const { return maxSize(); } bool init(const Buffer &seed, uint32_t numThreads); - std::pair hugePages() const; + std::pair hugePages(bool cache = true) const; void *raw() const; static inline constexpr size_t maxSize() { return RANDOMX_DATASET_MAX_SIZE; } private: + void allocate(bool hugePages); + Algorithm m_algorithm; int m_flags = 0; randomx_dataset *m_dataset = nullptr; From 7508411faf94582bd314f0240a91e744a1aca46c Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 4 Oct 2019 10:49:55 +0700 Subject: [PATCH 04/11] Extended "numa" option for RandomX. --- cmake/randomx.cmake | 10 +++ src/backend/cpu/platform/HwlocCpuInfo.cpp | 5 +- src/backend/cpu/platform/HwlocCpuInfo.h | 7 +- src/crypto/rx/Rx.cpp | 6 +- src/crypto/rx/RxConfig.cpp | 45 ---------- src/crypto/rx/RxConfig.h | 12 ++- src/crypto/rx/RxConfig_basic.cpp | 59 +++++++++++++ src/crypto/rx/RxConfig_hwloc.cpp | 100 ++++++++++++++++++++++ 8 files changed, 189 insertions(+), 55 deletions(-) create mode 100644 src/crypto/rx/RxConfig_basic.cpp create mode 100644 src/crypto/rx/RxConfig_hwloc.cpp diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake index 6c150d491..40727e93f 100644 --- a/cmake/randomx.cmake +++ b/cmake/randomx.cmake @@ -66,6 +66,16 @@ if (WITH_RANDOMX) if (CMAKE_CXX_COMPILER_ID MATCHES Clang) set_source_files_properties(src/crypto/randomx/jit_compiler_x86.cpp PROPERTIES COMPILE_FLAGS -Wno-unused-const-variable) endif() + + if (WITH_HWLOC) + list(APPEND SOURCES_CRYPTO + src/crypto/rx/RxConfig_hwloc.cpp + ) + else() + list(APPEND SOURCES_CRYPTO + src/crypto/rx/RxConfig_basic.cpp + ) + endif() else() remove_definitions(/DXMRIG_ALGO_RANDOMX) endif() diff --git a/src/backend/cpu/platform/HwlocCpuInfo.cpp b/src/backend/cpu/platform/HwlocCpuInfo.cpp index c9a45e1d5..dfb946863 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.cpp +++ b/src/backend/cpu/platform/HwlocCpuInfo.cpp @@ -46,7 +46,6 @@ namespace xmrig { -std::vector HwlocCpuInfo::m_nodeIndexes; uint32_t HwlocCpuInfo::m_features = 0; @@ -185,11 +184,11 @@ xmrig::HwlocCpuInfo::HwlocCpuInfo() m_features |= SET_THISTHREAD_MEMBIND; } - m_nodeIndexes.reserve(m_nodes); + m_nodeset.reserve(m_nodes); hwloc_obj_t node = nullptr; while ((node = hwloc_get_next_obj_by_type(m_topology, HWLOC_OBJ_NUMANODE, node)) != nullptr) { - m_nodeIndexes.emplace_back(node->os_index); + m_nodeset.emplace_back(node->os_index); } } } diff --git a/src/backend/cpu/platform/HwlocCpuInfo.h b/src/backend/cpu/platform/HwlocCpuInfo.h index ec4aea2cb..b796afb22 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.h +++ b/src/backend/cpu/platform/HwlocCpuInfo.h @@ -52,7 +52,9 @@ public: ~HwlocCpuInfo() override; static inline bool has(Feature feature) { return m_features & feature; } - static inline const std::vector &nodeIndexes() { return m_nodeIndexes; } + + inline const std::vector &nodeset() const { return m_nodeset; } + inline hwloc_topology_t topology() const { return m_topology; } protected: CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const override; @@ -67,7 +69,7 @@ protected: private: void processTopLevelCache(hwloc_obj_t obj, const Algorithm &algorithm, CpuThreads &threads, size_t limit) const; - static std::vector m_nodeIndexes; + static uint32_t m_features; char m_backend[20] = { 0 }; @@ -76,6 +78,7 @@ private: size_t m_cores = 0; size_t m_nodes = 0; size_t m_packages = 0; + std::vector m_nodeset; }; diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index f688a9dd7..3e8d06e07 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -104,13 +104,13 @@ public: } - static void initDataset(const RxSeed &seed, uint32_t threads, bool hugePages) + static void initDataset(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages) { std::lock_guard lock(mutex); LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."), tag, - false ? "s" : "", // FIXME + nodeset.size() > 1 ? "s" : "", seed.algorithm().shortName(), threads, Buffer::toHex(seed.data().data(), 8).data() @@ -168,7 +168,7 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, IRx d_ptr->setState(job, listener); - std::thread thread(RxPrivate::initDataset, job, config.threads(), hugePages); + std::thread thread(RxPrivate::initDataset, job, config.nodeset(), config.threads(), hugePages); thread.detach(); return false; diff --git a/src/crypto/rx/RxConfig.cpp b/src/crypto/rx/RxConfig.cpp index 63f60b3bf..07f45eac9 100644 --- a/src/crypto/rx/RxConfig.cpp +++ b/src/crypto/rx/RxConfig.cpp @@ -25,51 +25,6 @@ #include "crypto/rx/RxConfig.h" #include "backend/cpu/Cpu.h" -#include "base/io/json/Json.h" -#include "rapidjson/document.h" - - -namespace xmrig { - -static const char *kInit = "init"; -static const char *kNUMA = "numa"; - -} - - -rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const -{ - using namespace rapidjson; - auto &allocator = doc.GetAllocator(); - - Value obj(kObjectType); - - obj.AddMember(StringRef(kInit), m_threads, allocator); - obj.AddMember(StringRef(kNUMA), m_numa, allocator); - - return obj; -} - - -bool xmrig::RxConfig::read(const rapidjson::Value &value) -{ - if (value.IsObject()) { - m_numa = Json::getBool(value, kNUMA, m_numa); - m_threads = Json::getInt(value, kInit, m_threads); - - return true; - } - - return false; -} - - -#ifdef XMRIG_FEATURE_HWLOC -bool xmrig::RxConfig::isNUMA() const -{ - return m_numa && Cpu::info()->nodes() > 1; -} -#endif uint32_t xmrig::RxConfig::threads() const diff --git a/src/crypto/rx/RxConfig.h b/src/crypto/rx/RxConfig.h index 343a884b0..52a832a22 100644 --- a/src/crypto/rx/RxConfig.h +++ b/src/crypto/rx/RxConfig.h @@ -29,6 +29,9 @@ #include "rapidjson/fwd.h" +#include + + namespace xmrig { @@ -39,9 +42,9 @@ public: rapidjson::Value toJSON(rapidjson::Document &doc) const; # ifdef XMRIG_FEATURE_HWLOC - bool isNUMA() const; + std::vector nodeset() const; # else - inline constexpr bool isNUMA() const { return false; } + inline std::vector nodeset() const { return std::vector(); } # endif uint32_t threads() const; @@ -49,6 +52,11 @@ public: private: bool m_numa = true; int m_threads = -1; + +# ifdef XMRIG_FEATURE_HWLOC + std::vector m_nodeset; +# endif + }; diff --git a/src/crypto/rx/RxConfig_basic.cpp b/src/crypto/rx/RxConfig_basic.cpp new file mode 100644 index 000000000..26ef7a90c --- /dev/null +++ b/src/crypto/rx/RxConfig_basic.cpp @@ -0,0 +1,59 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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 "crypto/rx/RxConfig.h" +#include "base/io/json/Json.h" +#include "rapidjson/document.h" + + +namespace xmrig { + +static const char *kInit = "init"; + +} + + +rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + obj.AddMember(StringRef(kInit), m_threads, allocator); + + return obj; +} + + +bool xmrig::RxConfig::read(const rapidjson::Value &value) +{ + if (value.IsObject()) { + m_threads = Json::getInt(value, kInit, m_threads); + + return true; + } + + return false; +} diff --git a/src/crypto/rx/RxConfig_hwloc.cpp b/src/crypto/rx/RxConfig_hwloc.cpp new file mode 100644 index 000000000..66f086f2f --- /dev/null +++ b/src/crypto/rx/RxConfig_hwloc.cpp @@ -0,0 +1,100 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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 "backend/cpu/Cpu.h" +#include "backend/cpu/platform/HwlocCpuInfo.h" +#include "base/io/json/Json.h" +#include "crypto/rx/RxConfig.h" +#include "rapidjson/document.h" + + +namespace xmrig { + +static const char *kInit = "init"; +static const char *kNUMA = "numa"; + +} + + +rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + + obj.AddMember(StringRef(kInit), m_threads, allocator); + + if (!m_nodeset.empty()) { + Value numa(kArrayType); + + for (uint32_t i : m_nodeset) { + numa.PushBack(i, allocator); + } + + obj.AddMember(StringRef(kNUMA), numa, allocator); + } + else { + obj.AddMember(StringRef(kNUMA), m_numa, allocator); + } + + return obj; +} + + +bool xmrig::RxConfig::read(const rapidjson::Value &value) +{ + if (value.IsObject()) { + m_threads = Json::getInt(value, kInit, m_threads); + + const auto &numa = Json::getValue(value, kNUMA); + if (numa.IsArray()) { + m_nodeset.reserve(numa.Size()); + + for (const auto &node : numa.GetArray()) { + if (node.IsUint()) { + m_nodeset.emplace_back(node.GetUint()); + } + } + } + else if (numa.IsBool()) { + m_numa = numa.GetBool(); + } + + return true; + } + + return false; +} + + +std::vector xmrig::RxConfig::nodeset() const +{ + if (!m_nodeset.empty()) { + return m_nodeset; + } + + return (m_numa && Cpu::info()->nodes() > 1) ? static_cast(Cpu::info())->nodeset() : std::vector(); +} From 207dae418d533d94961c9b7f413613c69a10058e Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 4 Oct 2019 18:43:03 +0700 Subject: [PATCH 05/11] Added RxNUMAStorage stub. --- cmake/randomx.cmake | 5 + src/backend/cpu/platform/HwlocCpuInfo.cpp | 14 ++ src/backend/cpu/platform/HwlocCpuInfo.h | 7 +- src/crypto/rx/Rx.cpp | 19 ++- src/crypto/rx/RxBasicStorage.h | 6 +- src/crypto/rx/RxNUMAStorage.cpp | 166 ++++++++++++++++++++++ src/crypto/rx/RxNUMAStorage.h | 66 +++++++++ 7 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 src/crypto/rx/RxNUMAStorage.cpp create mode 100644 src/crypto/rx/RxNUMAStorage.h diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake index 40727e93f..27b9d9f60 100644 --- a/cmake/randomx.cmake +++ b/cmake/randomx.cmake @@ -68,8 +68,13 @@ if (WITH_RANDOMX) endif() if (WITH_HWLOC) + list(APPEND SOURCES_CRYPTO + src/crypto/rx/RxNUMAStorage.h + ) + list(APPEND SOURCES_CRYPTO src/crypto/rx/RxConfig_hwloc.cpp + src/crypto/rx/RxNUMAStorage.cpp ) else() list(APPEND SOURCES_CRYPTO diff --git a/src/backend/cpu/platform/HwlocCpuInfo.cpp b/src/backend/cpu/platform/HwlocCpuInfo.cpp index dfb946863..3983e8b02 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.cpp +++ b/src/backend/cpu/platform/HwlocCpuInfo.cpp @@ -200,6 +200,20 @@ xmrig::HwlocCpuInfo::~HwlocCpuInfo() } +bool xmrig::HwlocCpuInfo::membind(hwloc_const_bitmap_t nodeset) +{ + if (!hwloc_topology_get_support(m_topology)->membind->set_thisthread_membind) { + return false; + } + +# if HWLOC_API_VERSION >= 0x20000 + return hwloc_set_membind(m_topology, nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET) >= 0; +# else + return hwloc_set_membind_nodeset(m_topology, nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD) >= 0; +# endif +} + + xmrig::CpuThreads xmrig::HwlocCpuInfo::threads(const Algorithm &algorithm, uint32_t limit) const { if (L2() == 0 && L3() == 0) { diff --git a/src/backend/cpu/platform/HwlocCpuInfo.h b/src/backend/cpu/platform/HwlocCpuInfo.h index b796afb22..c22291e85 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.h +++ b/src/backend/cpu/platform/HwlocCpuInfo.h @@ -30,8 +30,9 @@ #include "base/tools/Object.h" -using hwloc_obj_t = struct hwloc_obj *; -using hwloc_topology_t = struct hwloc_topology *; +using hwloc_const_bitmap_t = const struct hwloc_bitmap_s *; +using hwloc_obj_t = struct hwloc_obj *; +using hwloc_topology_t = struct hwloc_topology *; namespace xmrig { @@ -56,6 +57,8 @@ public: inline const std::vector &nodeset() const { return m_nodeset; } inline hwloc_topology_t topology() const { return m_topology; } + bool membind(hwloc_const_bitmap_t nodeset); + protected: CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const override; diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 3e8d06e07..708a9559c 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -46,6 +46,11 @@ #include "crypto/rx/RxSeed.h" +#ifdef XMRIG_FEATURE_HWLOC +# include "crypto/rx/RxNUMAStorage.h" +#endif + + #include #include #include @@ -94,10 +99,18 @@ public: inline void asyncSend() { --m_pending; if (pending() == 0) { uv_async_send(m_async); } } - inline IRxStorage *storage() + inline IRxStorage *storage(const std::vector &nodeset) { if (!m_storage) { - m_storage = new RxBasicStorage(); +# ifdef XMRIG_FEATURE_HWLOC + if (!nodeset.empty()) { + m_storage = new RxNUMAStorage(nodeset); + } + else +# endif + { + m_storage = new RxBasicStorage(); + } } return m_storage; @@ -116,7 +129,7 @@ public: Buffer::toHex(seed.data().data(), 8).data() ); - d_ptr->storage()->init(seed, threads, hugePages); + d_ptr->storage(nodeset)->init(seed, threads, hugePages); d_ptr->asyncSend(); } diff --git a/src/crypto/rx/RxBasicStorage.h b/src/crypto/rx/RxBasicStorage.h index d6dad10dc..63eba1d95 100644 --- a/src/crypto/rx/RxBasicStorage.h +++ b/src/crypto/rx/RxBasicStorage.h @@ -24,8 +24,8 @@ * along with this program. If not, see . */ -#ifndef XMRIG_RX_VM_H -#define XMRIG_RX_VM_H +#ifndef XMRIG_RX_BASICSTORAGE_H +#define XMRIG_RX_BASICSTORAGE_H #include "backend/common/interfaces/IRxStorage.h" @@ -60,4 +60,4 @@ private: } /* namespace xmrig */ -#endif /* XMRIG_RX_VM_H */ +#endif /* XMRIG_RX_BASICSTORAGE_H */ diff --git a/src/crypto/rx/RxNUMAStorage.cpp b/src/crypto/rx/RxNUMAStorage.cpp new file mode 100644 index 000000000..3d5b999a9 --- /dev/null +++ b/src/crypto/rx/RxNUMAStorage.cpp @@ -0,0 +1,166 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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 "crypto/rx/RxNUMAStorage.h" +#include "backend/common/Tags.h" +#include "base/io/log/Log.h" +#include "base/tools/Chrono.h" +#include "crypto/rx/RxAlgo.h" +#include "crypto/rx/RxCache.h" +#include "crypto/rx/RxDataset.h" +#include "crypto/rx/RxSeed.h" + + +#include + + +namespace xmrig { + + +constexpr size_t oneMiB = 1024 * 1024; + + +class RxNUMAStoragePrivate +{ +public: + inline bool isReady(const Job &job) const { return m_ready && m_seed == job; } + inline RxDataset *dataset() const { return m_dataset; } + + + inline void setSeed(const RxSeed &seed) + { + m_ready = false; + + if (m_seed.algorithm() != seed.algorithm()) { + RxAlgo::apply(seed.algorithm()); + } + + m_seed = seed; + } + + + inline void createDataset(bool hugePages) + { + const uint64_t ts = Chrono::steadyMSecs(); + + m_dataset = new RxDataset(hugePages, true); + printAllocStatus(ts); + } + + + inline void initDataset(uint32_t threads, uint64_t ts) + { + m_dataset->init(m_seed.data(), threads); + + LOG_INFO("%s" GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); + + m_ready = true; + } + + +private: + void printAllocStatus(uint64_t ts) + { + if (m_dataset->get() != nullptr) { + const auto pages = m_dataset->hugePages(); + const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + + LOG_INFO("%s" GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu)") " huge pages %s%u/%u %1.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), + rx_tag(), + (RxDataset::maxSize() + RxCache::maxSize()) / oneMiB, + RxDataset::maxSize() / oneMiB, + RxCache::maxSize() / oneMiB, + (pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + pages.first, + pages.second, + percent, + m_dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", + Chrono::steadyMSecs() - ts + ); + } + else { + LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "failed to allocate RandomX dataset, switching to slow mode" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); + } + } + + + bool m_ready = false; + RxDataset *m_dataset = nullptr; + RxSeed m_seed; + std::map m_datasets; + std::vector m_nodeset; +}; + + +} // namespace xmrig + + +xmrig::RxNUMAStorage::RxNUMAStorage(const std::vector &nodeset) : + d_ptr(new RxNUMAStoragePrivate()) +{ + LOG_WARN(">>>>>> %zu", nodeset.size()); // FIXME +} + + +xmrig::RxNUMAStorage::~RxNUMAStorage() +{ + delete d_ptr; +} + + +xmrig::RxDataset *xmrig::RxNUMAStorage::dataset(const Job &job, uint32_t) const +{ + if (!d_ptr->isReady(job)) { + return nullptr; + } + + return d_ptr->dataset(); +} + + +std::pair xmrig::RxNUMAStorage::hugePages() const +{ + if (!d_ptr->dataset()) { + return { 0u, 0u }; + } + + return d_ptr->dataset()->hugePages(); +} + + +void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages) +{ + const uint64_t ts = Chrono::steadyMSecs(); + + d_ptr->setSeed(seed); + + if (!d_ptr->dataset()) { + d_ptr->createDataset(hugePages); + } + + d_ptr->initDataset(threads, ts); +} diff --git a/src/crypto/rx/RxNUMAStorage.h b/src/crypto/rx/RxNUMAStorage.h new file mode 100644 index 000000000..3afdd81d9 --- /dev/null +++ b/src/crypto/rx/RxNUMAStorage.h @@ -0,0 +1,66 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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_RX_NUMASTORAGE_H +#define XMRIG_RX_NUMASTORAGE_H + + +#include "backend/common/interfaces/IRxStorage.h" +#include "base/tools/Object.h" + + +#include + + +namespace xmrig +{ + + +class RxNUMAStoragePrivate; + + +class RxNUMAStorage : public IRxStorage +{ +public: + XMRIG_DISABLE_COPY_MOVE(RxNUMAStorage); + + RxNUMAStorage(const std::vector &nodeset); + ~RxNUMAStorage() override; + +protected: + RxDataset *dataset(const Job &job, uint32_t nodeId) const override; + std::pair hugePages() const override; + void init(const RxSeed &seed, uint32_t threads, bool hugePages) override; + +private: + RxNUMAStoragePrivate *d_ptr; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_NUMASTORAGE_H */ From ad6dc876b3fbdcce0aab30cb3f9bd1a6f92d31ac Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 4 Oct 2019 19:52:15 +0700 Subject: [PATCH 06/11] Simplified VirtualMemory::bindToNUMANode. --- src/crypto/common/VirtualMemory.cpp | 47 ++++++++--------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/src/crypto/common/VirtualMemory.cpp b/src/crypto/common/VirtualMemory.cpp index 081b6c0fa..56cf3f5f1 100644 --- a/src/crypto/common/VirtualMemory.cpp +++ b/src/crypto/common/VirtualMemory.cpp @@ -28,57 +28,34 @@ #ifdef XMRIG_FEATURE_HWLOC # include # include "backend/cpu/platform/HwlocCpuInfo.h" -# -# if HWLOC_API_VERSION < 0x00010b00 -# define HWLOC_OBJ_NUMANODE HWLOC_OBJ_NODE -# endif #endif -#include "base/io/log/Log.h" #include "crypto/common/VirtualMemory.h" +#include "backend/cpu/Cpu.h" +#include "base/io/log/Log.h" + + +#include uint32_t xmrig::VirtualMemory::bindToNUMANode(int64_t affinity) { # ifdef XMRIG_FEATURE_HWLOC - if (affinity < 0 || !HwlocCpuInfo::has(HwlocCpuInfo::SET_THISTHREAD_MEMBIND)) { + if (affinity < 0 || Cpu::info()->nodes() < 2) { return 0; } - hwloc_topology_t topology; - hwloc_topology_init(&topology); - hwloc_topology_load(topology); + auto cpu = static_cast(Cpu::info()); + hwloc_obj_t pu = hwloc_get_pu_obj_by_os_index(cpu->topology(), static_cast(affinity)); - const unsigned puId = static_cast(affinity); + if (pu == nullptr || !cpu->membind(pu->nodeset)) { + LOG_WARN("CPU #%02" PRId64 " warning: \"can't bind memory\"", affinity); - hwloc_obj_t pu = hwloc_get_pu_obj_by_os_index(topology, puId); - -# if HWLOC_API_VERSION >= 0x20000 - if (pu == nullptr || hwloc_set_membind(topology, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET) < 0) { -# else - if (pu == nullptr || hwloc_set_membind_nodeset(topology, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD) < 0) { -# endif - LOG_WARN("CPU #%02u warning: \"can't bind memory\"", puId); + return 0; } - uint32_t nodeId = 0; - - if (pu) { - hwloc_obj_t node = nullptr; - - while ((node = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, node)) != nullptr) { - if (hwloc_bitmap_intersects(node->cpuset, pu->cpuset)) { - nodeId = node->os_index; - - break; - } - } - } - - hwloc_topology_destroy(topology); - - return nodeId; + return hwloc_bitmap_first(pu->nodeset); # else return 0; # endif From 05928ccc250060059d577dabee087212e74c7f41 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sat, 5 Oct 2019 08:24:28 +0700 Subject: [PATCH 07/11] Implemented RxNUMAStorage. --- src/backend/cpu/CpuBackend.cpp | 4 +- .../opencl/runners/OclRxBaseRunner.cpp | 2 +- src/crypto/rx/RxBasicStorage.cpp | 23 +- src/crypto/rx/RxCache.cpp | 17 +- src/crypto/rx/RxCache.h | 2 + src/crypto/rx/RxDataset.cpp | 32 +++ src/crypto/rx/RxDataset.h | 12 +- src/crypto/rx/RxNUMAStorage.cpp | 267 +++++++++++++++--- src/crypto/rx/RxVm.cpp | 8 +- 9 files changed, 306 insertions(+), 61 deletions(-) diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp index d6d3ff14c..78b71e25a 100644 --- a/src/backend/cpu/CpuBackend.cpp +++ b/src/backend/cpu/CpuBackend.cpp @@ -111,13 +111,13 @@ public: return; } - LOG_INFO("%s" GREEN_BOLD(" READY") " threads %s%zu/%zu (%zu)" CLEAR " huge pages %s%zu/%zu %1.0f%%" CLEAR " memory " CYAN_BOLD("%zu KB") BLACK_BOLD(" (%" PRIu64 " ms)"), + LOG_INFO("%s" GREEN_BOLD(" READY") " threads %s%zu/%zu (%zu)" CLEAR " huge pages %s%1.0f%% %zu/%zu" CLEAR " memory " CYAN_BOLD("%zu KB") BLACK_BOLD(" (%" PRIu64 " ms)"), tag, m_errors == 0 ? CYAN_BOLD_S : YELLOW_BOLD_S, m_started, m_threads, m_ways, (m_hugePages == m_pages ? GREEN_BOLD_S : (m_hugePages == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), - m_hugePages, m_pages, m_hugePages == 0 ? 0.0 : static_cast(m_hugePages) / m_pages * 100.0, + m_hugePages, m_pages, memory() / 1024, Chrono::steadyMSecs() - m_ts ); diff --git a/src/backend/opencl/runners/OclRxBaseRunner.cpp b/src/backend/opencl/runners/OclRxBaseRunner.cpp index fa0259b49..278c4f768 100644 --- a/src/backend/opencl/runners/OclRxBaseRunner.cpp +++ b/src/backend/opencl/runners/OclRxBaseRunner.cpp @@ -122,7 +122,7 @@ void xmrig::OclRxBaseRunner::set(const Job &job, uint8_t *blob) m_seed = job.seed(); auto dataset = Rx::dataset(job, 0); - enqueueWriteBuffer(m_dataset, CL_TRUE, 0, dataset->size(), dataset->raw()); + enqueueWriteBuffer(m_dataset, CL_TRUE, 0, RxDataset::maxSize(), dataset->raw()); } if (job.size() < Job::kMaxBlobSize) { diff --git a/src/crypto/rx/RxBasicStorage.cpp b/src/crypto/rx/RxBasicStorage.cpp index 6b1509cc8..dcabad5b0 100644 --- a/src/crypto/rx/RxBasicStorage.cpp +++ b/src/crypto/rx/RxBasicStorage.cpp @@ -29,6 +29,7 @@ #include "backend/common/Tags.h" #include "base/io/log/Log.h" #include "base/tools/Chrono.h" +#include "base/tools/Object.h" #include "crypto/rx/RxAlgo.h" #include "crypto/rx/RxCache.h" #include "crypto/rx/RxDataset.h" @@ -44,6 +45,14 @@ constexpr size_t oneMiB = 1024 * 1024; class RxBasicStoragePrivate { public: + XMRIG_DISABLE_COPY_MOVE(RxBasicStoragePrivate) + + inline RxBasicStoragePrivate() = default; + inline ~RxBasicStoragePrivate() + { + delete m_dataset; + } + inline bool isReady(const Job &job) const { return m_ready && m_seed == job; } inline RxDataset *dataset() const { return m_dataset; } @@ -69,8 +78,10 @@ public: } - inline void initDataset(uint32_t threads, uint64_t ts) + inline void initDataset(uint32_t threads) { + const uint64_t ts = Chrono::steadyMSecs(); + m_dataset->init(m_seed.data(), threads); LOG_INFO("%s" GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); @@ -86,15 +97,15 @@ private: const auto pages = m_dataset->hugePages(); const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; - LOG_INFO("%s" GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu)") " huge pages %s%u/%u %1.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), + LOG_INFO("%s" GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu)") " huge pages %s%1.0f%% %u/%u" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), - (RxDataset::maxSize() + RxCache::maxSize()) / oneMiB, + m_dataset->size() / oneMiB, RxDataset::maxSize() / oneMiB, RxCache::maxSize() / oneMiB, (pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + percent, pages.first, pages.second, - percent, m_dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", Chrono::steadyMSecs() - ts ); @@ -148,13 +159,11 @@ std::pair xmrig::RxBasicStorage::hugePages() const void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages) { - const uint64_t ts = Chrono::steadyMSecs(); - d_ptr->setSeed(seed); if (!d_ptr->dataset()) { d_ptr->createDataset(hugePages); } - d_ptr->initDataset(threads, ts); + d_ptr->initDataset(threads); } diff --git a/src/crypto/rx/RxCache.cpp b/src/crypto/rx/RxCache.cpp index e8fcb6857..a248ea5cc 100644 --- a/src/crypto/rx/RxCache.cpp +++ b/src/crypto/rx/RxCache.cpp @@ -25,8 +25,9 @@ */ -#include "crypto/randomx/randomx.h" #include "crypto/rx/RxCache.h" +#include "crypto/common/VirtualMemory.h" +#include "crypto/randomx/randomx.h" static_assert(RANDOMX_FLAG_JIT == 8, "RANDOMX_FLAG_JIT flag mismatch"); @@ -72,3 +73,17 @@ bool xmrig::RxCache::init(const Buffer &seed) return true; } + + +std::pair xmrig::RxCache::hugePages() const +{ + constexpr size_t twoMiB = 2u * 1024u * 1024u; + constexpr size_t total = VirtualMemory::align(maxSize(), twoMiB) / twoMiB; + + uint32_t count = 0; + if (isHugePages()) { + count += total; + } + + return { count, total }; +} diff --git a/src/crypto/rx/RxCache.h b/src/crypto/rx/RxCache.h index f01e50872..84635292b 100644 --- a/src/crypto/rx/RxCache.h +++ b/src/crypto/rx/RxCache.h @@ -55,8 +55,10 @@ public: inline bool isJIT() const { return m_flags & 8; } inline const Buffer &seed() const { return m_seed; } inline randomx_cache *get() const { return m_cache; } + inline size_t size() const { return maxSize(); } bool init(const Buffer &seed); + std::pair hugePages() const; static inline constexpr size_t maxSize() { return RANDOMX_CACHE_MAX_SIZE; } diff --git a/src/crypto/rx/RxDataset.cpp b/src/crypto/rx/RxDataset.cpp index f5572c2a8..62887a01c 100644 --- a/src/crypto/rx/RxDataset.cpp +++ b/src/crypto/rx/RxDataset.cpp @@ -48,6 +48,12 @@ xmrig::RxDataset::RxDataset(bool hugePages, bool cache) } +xmrig::RxDataset::RxDataset(RxCache *cache) : + m_cache(cache) +{ +} + + xmrig::RxDataset::~RxDataset() { if (m_dataset) { @@ -94,6 +100,22 @@ bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads) } +size_t xmrig::RxDataset::size(bool cache) const +{ + size_t size = 0; + + if (m_dataset) { + size += maxSize(); + } + + if (cache && m_cache) { + size += RxCache::maxSize(); + } + + return size; +} + + std::pair xmrig::RxDataset::hugePages(bool cache) const { constexpr size_t twoMiB = 2u * 1024u * 1024u; @@ -123,6 +145,16 @@ void *xmrig::RxDataset::raw() const } +void xmrig::RxDataset::setRaw(const void *raw) +{ + if (!m_dataset) { + return; + } + + memcpy(randomx_get_dataset_memory(m_dataset), raw, maxSize()); +} + + void xmrig::RxDataset::allocate(bool hugePages) { if (hugePages) { diff --git a/src/crypto/rx/RxDataset.h b/src/crypto/rx/RxDataset.h index 5ca10e004..9b4f41204 100644 --- a/src/crypto/rx/RxDataset.h +++ b/src/crypto/rx/RxDataset.h @@ -50,23 +50,25 @@ public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxDataset) RxDataset(bool hugePages, bool cache); + RxDataset(RxCache *cache); ~RxDataset(); - inline bool isHugePages() const { return m_flags & 1; } - inline randomx_dataset *get() const { return m_dataset; } - inline RxCache *cache() const { return m_cache; } - inline size_t size() const { return maxSize(); } + inline bool isHugePages() const { return m_flags & 1; } + inline randomx_dataset *get() const { return m_dataset; } + inline RxCache *cache() const { return m_cache; } + inline void setCache(RxCache *cache) { m_cache = cache; } bool init(const Buffer &seed, uint32_t numThreads); + size_t size(bool cache = true) const; std::pair hugePages(bool cache = true) const; void *raw() const; + void setRaw(const void *raw); static inline constexpr size_t maxSize() { return RANDOMX_DATASET_MAX_SIZE; } private: void allocate(bool hugePages); - Algorithm m_algorithm; int m_flags = 0; randomx_dataset *m_dataset = nullptr; RxCache *m_cache = nullptr; diff --git a/src/crypto/rx/RxNUMAStorage.cpp b/src/crypto/rx/RxNUMAStorage.cpp index 3d5b999a9..f6fffadd8 100644 --- a/src/crypto/rx/RxNUMAStorage.cpp +++ b/src/crypto/rx/RxNUMAStorage.cpp @@ -27,8 +27,12 @@ #include "crypto/rx/RxNUMAStorage.h" #include "backend/common/Tags.h" +#include "backend/cpu/Cpu.h" +#include "backend/cpu/platform/HwlocCpuInfo.h" #include "base/io/log/Log.h" +#include "base/kernel/Platform.h" #include "base/tools/Chrono.h" +#include "base/tools/Object.h" #include "crypto/rx/RxAlgo.h" #include "crypto/rx/RxCache.h" #include "crypto/rx/RxDataset.h" @@ -36,19 +40,64 @@ #include +#include +#include +#include namespace xmrig { constexpr size_t oneMiB = 1024 * 1024; +static std::mutex mutex; + + +static bool bindToNUMANode(uint32_t nodeId) +{ + auto cpu = static_cast(Cpu::info()); + hwloc_obj_t node = hwloc_get_numanode_obj_by_os_index(cpu->topology(), nodeId); + if (!node) { + return false; + } + + if (cpu->membind(node->nodeset)) { + Platform::setThreadAffinity(static_cast(hwloc_bitmap_first(node->cpuset))); + + return true; + } + + return false; +} + + +static inline void printSkipped(uint32_t nodeId, const char *reason) +{ + LOG_WARN("%s" CYAN_BOLD("#%u ") RED_BOLD("skipped") YELLOW(" (%s)"), rx_tag(), nodeId, reason); +} + + +static inline void printDatasetReady(uint32_t nodeId, uint64_t ts) +{ + LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), nodeId, Chrono::steadyMSecs() - ts); +} class RxNUMAStoragePrivate { public: - inline bool isReady(const Job &job) const { return m_ready && m_seed == job; } - inline RxDataset *dataset() const { return m_dataset; } + XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxNUMAStoragePrivate) + + inline RxNUMAStoragePrivate(const std::vector &nodeset) : m_nodeset(nodeset) {} + inline ~RxNUMAStoragePrivate() + { + for (auto const &item : m_datasets) { + delete item.second; + } + } + + inline bool isAllocated() const { return m_allocated; } + inline bool isReady(const Job &job) const { return m_ready && m_seed == job; } + inline RxDataset *dataset(uint32_t nodeId) const { return m_datasets.count(nodeId) ? m_datasets.at(nodeId) : m_datasets.at(m_nodeset.front()); } inline void setSeed(const RxSeed &seed) @@ -63,53 +112,190 @@ public: } - inline void createDataset(bool hugePages) + inline void createDatasets(bool hugePages) { const uint64_t ts = Chrono::steadyMSecs(); - m_dataset = new RxDataset(hugePages, true); - printAllocStatus(ts); + std::vector threads; + threads.reserve(m_nodeset.size()); + + for (uint32_t node : m_nodeset) { + threads.emplace_back(allocate, this, node, hugePages); + } + + for (auto &thread : threads) { + thread.join(); + } + + std::thread thread(allocateCache, this, m_nodeset.front(), hugePages); + thread.join(); + + if (m_datasets.empty()) { + m_datasets.insert({ m_nodeset.front(), new RxDataset(m_cache) }); + + LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "failed to allocate RandomX datasets, switching to slow mode" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); + } + else { + dataset(m_nodeset.front())->setCache(m_cache); + + printAllocStatus(ts); + } + + m_allocated = true; } - inline void initDataset(uint32_t threads, uint64_t ts) + inline void initDatasets(uint32_t threads) { - m_dataset->init(m_seed.data(), threads); + uint64_t ts = Chrono::steadyMSecs(); + auto id = m_nodeset.front(); + auto primary = dataset(id); - LOG_INFO("%s" GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); + primary->init(m_seed.data(), threads); + + printDatasetReady(id, ts); + + if (m_datasets.size() > 1) { + std::vector threads; + threads.reserve(m_datasets.size() - 1); + + for (auto const &item : m_datasets) { + if (item.first == id) { + continue; + } + + threads.emplace_back(copyDataset, item.second, item.first, primary->raw()); + } + + for (auto &thread : threads) { + thread.join(); + } + } m_ready = true; } -private: - void printAllocStatus(uint64_t ts) + inline std::pair hugePages() const { - if (m_dataset->get() != nullptr) { - const auto pages = m_dataset->hugePages(); - const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + auto pages = m_cache->hugePages(); + for (auto const &item : m_datasets) { + const auto p = item.second->hugePages(false); + pages.first += p.first; + pages.second += p.second; + } - LOG_INFO("%s" GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu)") " huge pages %s%u/%u %1.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), - rx_tag(), - (RxDataset::maxSize() + RxCache::maxSize()) / oneMiB, - RxDataset::maxSize() / oneMiB, - RxCache::maxSize() / oneMiB, - (pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), - pages.first, - pages.second, - percent, - m_dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", - Chrono::steadyMSecs() - ts - ); - } - else { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "failed to allocate RandomX dataset, switching to slow mode" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); - } + return pages; } - bool m_ready = false; - RxDataset *m_dataset = nullptr; +private: + static void allocate(RxNUMAStoragePrivate *d_ptr, uint32_t nodeId, bool hugePages) + { + const uint64_t ts = Chrono::steadyMSecs(); + + if (!bindToNUMANode(nodeId)) { + printSkipped(nodeId, "can't bind memory"); + + return; + } + + auto dataset = new RxDataset(hugePages, false); + if (!dataset->get()) { + printSkipped(nodeId, "failed to allocate dataset"); + + delete dataset; + return; + } + + std::lock_guard lock(mutex); + d_ptr->m_datasets.insert({ nodeId, dataset }); + d_ptr->printAllocStatus(dataset, nodeId, ts); + } + + + static void allocateCache(RxNUMAStoragePrivate *d_ptr, uint32_t nodeId, bool hugePages) + { + const uint64_t ts = Chrono::steadyMSecs(); + + bindToNUMANode(nodeId); + + auto cache = new RxCache(hugePages); + + std::lock_guard lock(mutex); + d_ptr->m_cache = cache; + d_ptr->printAllocStatus(cache, nodeId, ts); + } + + + static void copyDataset(RxDataset *dst, uint32_t nodeId, const void *raw) + { + const uint64_t ts = Chrono::steadyMSecs(); + + dst->setRaw(raw); + + printDatasetReady(nodeId, ts); + } + + + void printAllocStatus(RxDataset *dataset, uint32_t nodeId, uint64_t ts) + { + const auto pages = dataset->hugePages(); + const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + + LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") " huge pages %s%3.0f%%" CLEAR BLACK_BOLD(" (%" PRIu64 " ms)"), + rx_tag(), + nodeId, + dataset->size() / oneMiB, + (pages.first == pages.second ? GREEN_BOLD_S : RED_BOLD_S), + percent, + Chrono::steadyMSecs() - ts + ); + } + + + void printAllocStatus(RxCache *cache, uint32_t nodeId, uint64_t ts) + { + const auto pages = cache->hugePages(); + const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + + LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("allocated") CYAN_BOLD(" %4zu MB") " huge pages %s%3.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), + rx_tag(), + nodeId, + cache->size() / oneMiB, + (pages.first == pages.second ? GREEN_BOLD_S : RED_BOLD_S), + percent, + cache->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", + Chrono::steadyMSecs() - ts + ); + } + + + void printAllocStatus(uint64_t ts) + { + size_t memory = m_cache->size(); + auto pages = hugePages(); + const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + + for (auto const &item : m_datasets) { + memory += item.second->size(false); + } + + LOG_INFO("%s" CYAN_BOLD("-- ") GREEN_BOLD("allocated") CYAN_BOLD(" %4zu MB") " huge pages %s%3.0f%% %u/%u" CLEAR BLACK_BOLD(" (%" PRIu64 " ms)"), + rx_tag(), + memory / oneMiB, + (pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + percent, + pages.first, + pages.second, + Chrono::steadyMSecs() - ts + ); + } + + + bool m_allocated = false; + bool m_ready = false; + RxCache *m_cache = nullptr; RxSeed m_seed; std::map m_datasets; std::vector m_nodeset; @@ -120,9 +306,8 @@ private: xmrig::RxNUMAStorage::RxNUMAStorage(const std::vector &nodeset) : - d_ptr(new RxNUMAStoragePrivate()) + d_ptr(new RxNUMAStoragePrivate(nodeset)) { - LOG_WARN(">>>>>> %zu", nodeset.size()); // FIXME } @@ -132,35 +317,33 @@ xmrig::RxNUMAStorage::~RxNUMAStorage() } -xmrig::RxDataset *xmrig::RxNUMAStorage::dataset(const Job &job, uint32_t) const +xmrig::RxDataset *xmrig::RxNUMAStorage::dataset(const Job &job, uint32_t nodeId) const { if (!d_ptr->isReady(job)) { return nullptr; } - return d_ptr->dataset(); + return d_ptr->dataset(nodeId); } std::pair xmrig::RxNUMAStorage::hugePages() const { - if (!d_ptr->dataset()) { + if (!d_ptr->isAllocated()) { return { 0u, 0u }; } - return d_ptr->dataset()->hugePages(); + return d_ptr->hugePages(); } void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages) { - const uint64_t ts = Chrono::steadyMSecs(); - d_ptr->setSeed(seed); - if (!d_ptr->dataset()) { - d_ptr->createDataset(hugePages); + if (!d_ptr->isAllocated()) { + d_ptr->createDatasets(hugePages); } - d_ptr->initDataset(threads, ts); + d_ptr->initDatasets(threads); } diff --git a/src/crypto/rx/RxVm.cpp b/src/crypto/rx/RxVm.cpp index 6426443a4..526b5ce69 100644 --- a/src/crypto/rx/RxVm.cpp +++ b/src/crypto/rx/RxVm.cpp @@ -41,11 +41,13 @@ xmrig::RxVm::RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes) m_flags |= RANDOMX_FLAG_FULL_MEM; } - if (dataset->cache()->isJIT()) { + if (!dataset->cache() || dataset->cache()->isJIT()) { m_flags |= RANDOMX_FLAG_JIT; + m_vm = randomx_create_vm(static_cast(m_flags), nullptr, dataset->get(), scratchpad); + } + else { + m_vm = randomx_create_vm(static_cast(m_flags), dataset->cache()->get(), dataset->get(), scratchpad); } - - m_vm = randomx_create_vm(static_cast(m_flags), dataset->cache()->get(), dataset->get(), scratchpad); } From d5af5cf8f811888b67484443af50af9d655318b9 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sat, 5 Oct 2019 11:24:22 +0700 Subject: [PATCH 08/11] Fixed exit. --- src/backend/cpu/CpuWorker.cpp | 6 ++--- src/crypto/rx/Rx.cpp | 7 ++++++ src/crypto/rx/RxNUMAStorage.cpp | 39 ++++++++++++++++++++------------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp index a98027c0f..22651c35f 100644 --- a/src/backend/cpu/CpuWorker.cpp +++ b/src/backend/cpu/CpuWorker.cpp @@ -69,12 +69,12 @@ xmrig::CpuWorker::CpuWorker(size_t id, const CpuLaunchData &data) : template xmrig::CpuWorker::~CpuWorker() { - CnCtx::release(m_ctx, N); - delete m_memory; - # ifdef XMRIG_ALGO_RANDOMX delete m_vm; # endif + + CnCtx::release(m_ctx, N); + delete m_memory; } diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 708a9559c..0642b3cb8 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -86,6 +86,9 @@ public: inline ~RxPrivate() { + m_pending = std::numeric_limits::max(); + + std::lock_guard lock(mutex); Handle::close(m_async); delete m_storage; @@ -121,6 +124,10 @@ public: { std::lock_guard lock(mutex); + if (d_ptr->pending() > std::numeric_limits::max()) { + return; + } + LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."), tag, nodeset.size() > 1 ? "s" : "", diff --git a/src/crypto/rx/RxNUMAStorage.cpp b/src/crypto/rx/RxNUMAStorage.cpp index f6fffadd8..6d8ec167b 100644 --- a/src/crypto/rx/RxNUMAStorage.cpp +++ b/src/crypto/rx/RxNUMAStorage.cpp @@ -87,9 +87,17 @@ class RxNUMAStoragePrivate public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxNUMAStoragePrivate) - inline RxNUMAStoragePrivate(const std::vector &nodeset) : m_nodeset(nodeset) {} + inline RxNUMAStoragePrivate(const std::vector &nodeset) : + m_nodeset(nodeset) + { + m_threads.reserve(nodeset.size()); + } + + inline ~RxNUMAStoragePrivate() { + join(); + for (auto const &item : m_datasets) { delete item.second; } @@ -116,16 +124,11 @@ public: { const uint64_t ts = Chrono::steadyMSecs(); - std::vector threads; - threads.reserve(m_nodeset.size()); - for (uint32_t node : m_nodeset) { - threads.emplace_back(allocate, this, node, hugePages); + m_threads.emplace_back(allocate, this, node, hugePages); } - for (auto &thread : threads) { - thread.join(); - } + join(); std::thread thread(allocateCache, this, m_nodeset.front(), hugePages); thread.join(); @@ -156,20 +159,15 @@ public: printDatasetReady(id, ts); if (m_datasets.size() > 1) { - std::vector threads; - threads.reserve(m_datasets.size() - 1); - for (auto const &item : m_datasets) { if (item.first == id) { continue; } - threads.emplace_back(copyDataset, item.second, item.first, primary->raw()); + m_threads.emplace_back(copyDataset, item.second, item.first, primary->raw()); } - for (auto &thread : threads) { - thread.join(); - } + join(); } m_ready = true; @@ -293,11 +291,22 @@ private: } + inline void join() + { + for (auto &thread : m_threads) { + thread.join(); + } + + m_threads.clear(); + } + + bool m_allocated = false; bool m_ready = false; RxCache *m_cache = nullptr; RxSeed m_seed; std::map m_datasets; + std::vector m_threads; std::vector m_nodeset; }; From 59b62dcb77429f476b7440b1010793687a1d9ce7 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 6 Oct 2019 07:47:41 +0700 Subject: [PATCH 09/11] Added class RxQueue, class Rx now thin static wrapper on top of RxQueue. --- cmake/randomx.cmake | 2 + src/core/Miner.cpp | 18 ++-- src/crypto/rx/Rx.cpp | 145 +++--------------------------- src/crypto/rx/Rx.h | 4 +- src/crypto/rx/RxQueue.cpp | 181 ++++++++++++++++++++++++++++++++++++++ src/crypto/rx/RxQueue.h | 108 +++++++++++++++++++++++ 6 files changed, 313 insertions(+), 145 deletions(-) create mode 100644 src/crypto/rx/RxQueue.cpp create mode 100644 src/crypto/rx/RxQueue.h diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake index 27b9d9f60..d05ceb890 100644 --- a/cmake/randomx.cmake +++ b/cmake/randomx.cmake @@ -8,6 +8,7 @@ if (WITH_RANDOMX) src/crypto/rx/RxCache.h src/crypto/rx/RxConfig.h src/crypto/rx/RxDataset.h + src/crypto/rx/RxQueue.h src/crypto/rx/RxSeed.h src/crypto/rx/RxVm.h ) @@ -38,6 +39,7 @@ if (WITH_RANDOMX) src/crypto/rx/RxCache.cpp src/crypto/rx/RxConfig.cpp src/crypto/rx/RxDataset.cpp + src/crypto/rx/RxQueue.cpp src/crypto/rx/RxVm.cpp ) diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index cbba3de34..45a903bac 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -72,12 +72,8 @@ class MinerPrivate public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(MinerPrivate) - inline MinerPrivate(Controller *controller) : controller(controller) - { -# ifdef XMRIG_ALGO_RANDOMX - Rx::init(); -# endif - } + + inline MinerPrivate(Controller *controller) : controller(controller) {} inline ~MinerPrivate() @@ -232,9 +228,9 @@ public: # ifdef XMRIG_ALGO_RANDOMX - bool initRX(IRxListener *listener) + inline bool initRX() { - return Rx::init(job, controller->config()->rx(), controller->config()->cpu().isHugePages(), listener); + return Rx::init(job, controller->config()->rx(), controller->config()->cpu().isHugePages()); } # endif @@ -261,6 +257,10 @@ public: xmrig::Miner::Miner(Controller *controller) : d_ptr(new MinerPrivate(controller)) { +# ifdef XMRIG_ALGO_RANDOMX + Rx::init(this); +# endif + controller->addListener(this); # ifdef XMRIG_FEATURE_API @@ -402,7 +402,7 @@ void xmrig::Miner::setJob(const Job &job, bool donate) } # ifdef XMRIG_ALGO_RANDOMX - const bool ready = d_ptr->initRX(this); + const bool ready = d_ptr->initRX(); # else constexpr const bool ready = true; # endif diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 0642b3cb8..115c0f623 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -26,36 +26,10 @@ #include "crypto/rx/Rx.h" - -#include "backend/common/interfaces/IRxListener.h" -#include "backend/common/interfaces/IRxStorage.h" #include "backend/common/Tags.h" -#include "backend/cpu/Cpu.h" #include "base/io/log/Log.h" -#include "base/kernel/Platform.h" -#include "base/net/stratum/Job.h" -#include "base/tools/Buffer.h" -#include "base/tools/Chrono.h" -#include "base/tools/Handle.h" -#include "base/tools/Object.h" -#include "crypto/rx/RxAlgo.h" -#include "crypto/rx/RxBasicStorage.h" -#include "crypto/rx/RxCache.h" #include "crypto/rx/RxConfig.h" -#include "crypto/rx/RxDataset.h" -#include "crypto/rx/RxSeed.h" - - -#ifdef XMRIG_FEATURE_HWLOC -# include "crypto/rx/RxNUMAStorage.h" -#endif - - -#include -#include -#include -#include -#include +#include "crypto/rx/RxQueue.h" namespace xmrig { @@ -66,104 +40,14 @@ class RxPrivate; static const char *tag = BLUE_BG(WHITE_BOLD_S " rx ") " "; static RxPrivate *d_ptr = nullptr; -static std::mutex mutex; class RxPrivate { public: - XMRIG_DISABLE_COPY_MOVE(RxPrivate) + inline RxPrivate(IRxListener *listener) : queue(listener) {} - inline RxPrivate() : - m_pending(0) - { - m_async = new uv_async_t; - m_async->data = this; - - uv_async_init(uv_default_loop(), m_async, [](uv_async_t *handle) { static_cast(handle->data)->onReady(); }); - } - - - inline ~RxPrivate() - { - m_pending = std::numeric_limits::max(); - - std::lock_guard lock(mutex); - Handle::close(m_async); - - delete m_storage; - } - - - inline bool isReady(const Job &job) const { return pending() == 0 && m_seed == job; } - inline RxDataset *dataset(const Job &job, uint32_t nodeId) { return m_storage ? m_storage->dataset(job, nodeId) : nullptr; } - inline std::pair hugePages() { return m_storage ? m_storage->hugePages() : std::pair(0u, 0u); } - inline uint64_t pending() const { return m_pending.load(std::memory_order_relaxed); } - inline void asyncSend() { --m_pending; if (pending() == 0) { uv_async_send(m_async); } } - - - inline IRxStorage *storage(const std::vector &nodeset) - { - if (!m_storage) { -# ifdef XMRIG_FEATURE_HWLOC - if (!nodeset.empty()) { - m_storage = new RxNUMAStorage(nodeset); - } - else -# endif - { - m_storage = new RxBasicStorage(); - } - } - - return m_storage; - } - - - static void initDataset(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages) - { - std::lock_guard lock(mutex); - - if (d_ptr->pending() > std::numeric_limits::max()) { - return; - } - - LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."), - tag, - nodeset.size() > 1 ? "s" : "", - seed.algorithm().shortName(), - threads, - Buffer::toHex(seed.data().data(), 8).data() - ); - - d_ptr->storage(nodeset)->init(seed, threads, hugePages); - d_ptr->asyncSend(); - } - - - inline void setState(const Job &job, IRxListener *listener) - { - m_listener = listener; - m_seed = job; - - ++m_pending; - } - - -private: - inline void onReady() - { - if (m_listener && pending() == 0) { - m_listener->onDatasetReady(); - } - } - - - IRxListener *m_listener = nullptr; - IRxStorage *m_storage = nullptr; - RxSeed m_seed; - std::atomic m_pending; - uv_async_t *m_async = nullptr; + RxQueue queue; }; @@ -176,20 +60,17 @@ const char *xmrig::rx_tag() } -bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, IRxListener *listener) +bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages) { if (job.algorithm().family() != Algorithm::RANDOM_X) { return true; } - if (d_ptr->isReady(job)) { + if (isReady(job)) { return true; } - d_ptr->setState(job, listener); - - std::thread thread(RxPrivate::initDataset, job, config.nodeset(), config.threads(), hugePages); - thread.detach(); + d_ptr->queue.enqueue(job, config.nodeset(), config.threads(), hugePages); return false; } @@ -197,23 +78,19 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, IRx bool xmrig::Rx::isReady(const Job &job) { - return d_ptr->isReady(job); + return d_ptr->queue.isReady(job); } xmrig::RxDataset *xmrig::Rx::dataset(const Job &job, uint32_t nodeId) { - std::lock_guard lock(mutex); - - return d_ptr->dataset(job, nodeId); + return d_ptr->queue.dataset(job, nodeId); } std::pair xmrig::Rx::hugePages() { - std::lock_guard lock(mutex); - - return d_ptr->hugePages(); + return d_ptr->queue.hugePages(); } @@ -225,7 +102,7 @@ void xmrig::Rx::destroy() } -void xmrig::Rx::init() +void xmrig::Rx::init(IRxListener *listener) { - d_ptr = new RxPrivate(); + d_ptr = new RxPrivate(listener); } diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index df3194137..4a81f5d5b 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -46,12 +46,12 @@ class RxDataset; class Rx { public: - static bool init(const Job &job, const RxConfig &config, bool hugePages, IRxListener *listener); + static bool init(const Job &job, const RxConfig &config, bool hugePages); static bool isReady(const Job &job); static RxDataset *dataset(const Job &job, uint32_t nodeId); static std::pair hugePages(); static void destroy(); - static void init(); + static void init(IRxListener *listener); }; diff --git a/src/crypto/rx/RxQueue.cpp b/src/crypto/rx/RxQueue.cpp new file mode 100644 index 000000000..48a1f9797 --- /dev/null +++ b/src/crypto/rx/RxQueue.cpp @@ -0,0 +1,181 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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 "crypto/rx/RxQueue.h" +#include "backend/common/Tags.h" +#include "base/io/log/Log.h" +#include "crypto/rx/RxBasicStorage.h" +#include "base/tools/Handle.h" +#include "backend/common/interfaces/IRxListener.h" + + +#ifdef XMRIG_FEATURE_HWLOC +# include "crypto/rx/RxNUMAStorage.h" +#endif + + +xmrig::RxQueue::RxQueue(IRxListener *listener) : + m_listener(listener) +{ + m_async = new uv_async_t; + m_async->data = this; + + uv_async_init(uv_default_loop(), m_async, [](uv_async_t *handle) { static_cast(handle->data)->onReady(); }); + + m_thread = std::move(std::thread(&RxQueue::backgroundInit, this)); +} + + +xmrig::RxQueue::~RxQueue() +{ + std::unique_lock lock(m_mutex); + m_state = STATE_SHUTDOWN; + lock.unlock(); + + m_cv.notify_one(); + + m_thread.join(); + + delete m_storage; + + Handle::close(m_async); +} + + +bool xmrig::RxQueue::isReady(const Job &job) +{ + std::lock_guard lock(m_mutex); + + return isReadyUnsafe(job); +} + + +xmrig::RxDataset *xmrig::RxQueue::dataset(const Job &job, uint32_t nodeId) +{ + std::lock_guard lock(m_mutex); + + if (isReadyUnsafe(job)) { + return m_storage->dataset(job, nodeId); + } + + return nullptr; +} + + +std::pair xmrig::RxQueue::hugePages() +{ + std::lock_guard lock(m_mutex); + + return m_storage && m_state == STATE_IDLE ? m_storage->hugePages() : std::pair(0u, 0u); +} + + +void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages) +{ + std::unique_lock lock(m_mutex); + + if (!m_storage) { +# ifdef XMRIG_FEATURE_HWLOC + if (!nodeset.empty()) { + m_storage = new RxNUMAStorage(nodeset); + } + else +# endif + { + m_storage = new RxBasicStorage(); + } + } + + if (m_state == STATE_PENDING && m_seed == seed) { + return; + } + + m_queue.emplace_back(seed, nodeset, threads, hugePages); + m_seed = seed; + m_state = STATE_PENDING; + + lock.unlock(); + + m_cv.notify_one(); +} + + +bool xmrig::RxQueue::isReadyUnsafe(const Job &job) const +{ + return m_storage != nullptr && m_state == STATE_IDLE && m_seed == job; +} + + +void xmrig::RxQueue::backgroundInit() +{ + while (true) { + std::unique_lock lock(m_mutex); + + if (m_state == STATE_IDLE) { + m_cv.wait(lock, [this]{ return m_state != STATE_IDLE; }); + } + + if (m_state == STATE_SHUTDOWN) { + break; + } + + const auto item = m_queue.back(); + m_queue.clear(); + + lock.unlock(); + + LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."), + rx_tag(), + item.nodeset.size() > 1 ? "s" : "", + item.seed.algorithm().shortName(), + item.threads, + Buffer::toHex(item.seed.data().data(), 8).data() + ); + + m_storage->init(item.seed, item.threads, item.hugePages); + + lock = std::move(std::unique_lock(m_mutex)); + if (!m_queue.empty()) { + continue; + } + + m_state = STATE_IDLE; + uv_async_send(m_async); + } +} + + +void xmrig::RxQueue::onReady() +{ + std::unique_lock lock(m_mutex); + const bool ready = m_listener && m_state == STATE_IDLE; + lock.unlock(); + + if (ready) { + m_listener->onDatasetReady(); + } +} diff --git a/src/crypto/rx/RxQueue.h b/src/crypto/rx/RxQueue.h new file mode 100644 index 000000000..28407a87f --- /dev/null +++ b/src/crypto/rx/RxQueue.h @@ -0,0 +1,108 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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_RX_QUEUE_H +#define XMRIG_RX_QUEUE_H + + +#include "base/tools/Object.h" +#include "crypto/rx/RxSeed.h" + + +#include +#include +#include + + +using uv_async_t = struct uv_async_s; + + +namespace xmrig +{ + + +class IRxListener; +class IRxStorage; +class RxDataset; + + +class RxQueueItem +{ +public: + RxQueueItem(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages) : + hugePages(hugePages), + seed(seed), + nodeset(nodeset), + threads(threads) + {} + + const bool hugePages; + const RxSeed seed; + const std::vector nodeset; + const uint32_t threads; +}; + + +class RxQueue +{ +public: + XMRIG_DISABLE_COPY_MOVE(RxQueue); + + RxQueue(IRxListener *listener); + ~RxQueue(); + + bool isReady(const Job &job); + RxDataset *dataset(const Job &job, uint32_t nodeId); + std::pair hugePages(); + void enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages); + +private: + enum State { + STATE_IDLE, + STATE_PENDING, + STATE_SHUTDOWN + }; + + bool isReadyUnsafe(const Job &job) const; + void backgroundInit(); + void onReady(); + + IRxListener *m_listener = nullptr; + IRxStorage *m_storage = nullptr; + RxSeed m_seed; + State m_state = STATE_IDLE; + std::condition_variable m_cv; + std::mutex m_mutex; + std::thread m_thread; + std::vector m_queue; + uv_async_t *m_async = nullptr; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_QUEUE_H */ From 8af1075c9821c18272a6d8b9980b1bf62981a24f Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 6 Oct 2019 11:03:01 +0700 Subject: [PATCH 10/11] Removed uv_try_write for console log. --- src/base/io/log/Log.cpp | 17 ++++++++------- src/base/io/log/backends/ConsoleLog.cpp | 28 +++---------------------- src/base/io/log/backends/ConsoleLog.h | 4 +--- 3 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/base/io/log/Log.cpp b/src/base/io/log/Log.cpp index 250bc3c4c..2cd372423 100644 --- a/src/base/io/log/Log.cpp +++ b/src/base/io/log/Log.cpp @@ -31,10 +31,10 @@ #include +#include +#include #include -#include #include -#include #include #include @@ -42,6 +42,7 @@ #include "base/io/log/Log.h" #include "base/kernel/interfaces/ILogBackend.h" #include "base/tools/Chrono.h" +#include "base/tools/Object.h" namespace xmrig { @@ -67,10 +68,10 @@ static const char *colors_map[] = { class LogPrivate { public: - inline LogPrivate() : - m_buf() - { - } + XMRIG_DISABLE_COPY_MOVE(LogPrivate) + + + LogPrivate() = default; inline ~LogPrivate() @@ -134,7 +135,7 @@ private: const uint64_t ms = Chrono::currentMSecsSinceEpoch(); time_t now = ms / 1000; - tm stime; + tm stime{}; # ifdef _WIN32 localtime_s(&stime, &now); @@ -188,7 +189,7 @@ private: } - char m_buf[4096]; + char m_buf[4096]{}; std::mutex m_mutex; std::vector m_backends; }; diff --git a/src/base/io/log/backends/ConsoleLog.cpp b/src/base/io/log/backends/ConsoleLog.cpp index 34a7d66ba..c2cd74dce 100644 --- a/src/base/io/log/backends/ConsoleLog.cpp +++ b/src/base/io/log/backends/ConsoleLog.cpp @@ -47,7 +47,6 @@ xmrig::ConsoleLog::ConsoleLog() } uv_tty_set_mode(m_tty, UV_TTY_MODE_NORMAL); - m_stream = reinterpret_cast(m_tty); # ifdef WIN32 HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); @@ -68,25 +67,14 @@ xmrig::ConsoleLog::~ConsoleLog() } -void xmrig::ConsoleLog::print(int, const char *line, size_t, size_t size, bool colors) +void xmrig::ConsoleLog::print(int, const char *line, size_t, size_t, bool colors) { if (!m_tty || Log::colors != colors) { return; } -# ifdef _WIN32 - uv_buf_t buf = uv_buf_init(const_cast(line), static_cast(size)); -# else - uv_buf_t buf = uv_buf_init(const_cast(line), size); -# endif - - if (!isWritable()) { - fputs(line, stdout); - fflush(stdout); - } - else { - uv_try_write(m_stream, &buf, 1); - } + fputs(line, stdout); + fflush(stdout); } @@ -95,13 +83,3 @@ bool xmrig::ConsoleLog::isSupported() const const uv_handle_type type = uv_guess_handle(1); return type == UV_TTY || type == UV_NAMED_PIPE; } - - -bool xmrig::ConsoleLog::isWritable() const -{ - if (!m_stream || uv_is_writable(m_stream) != 1) { - return false; - } - - return isSupported(); -} diff --git a/src/base/io/log/backends/ConsoleLog.h b/src/base/io/log/backends/ConsoleLog.h index 6277cc7be..36610e5e8 100644 --- a/src/base/io/log/backends/ConsoleLog.h +++ b/src/base/io/log/backends/ConsoleLog.h @@ -51,10 +51,8 @@ protected: private: bool isSupported() const; - bool isWritable() const; - uv_stream_t *m_stream = nullptr; - uv_tty_t *m_tty = nullptr; + uv_tty_t *m_tty = nullptr; }; From 0a1836e5a80038428fa6710097d08db711271349 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 6 Oct 2019 11:19:32 +0700 Subject: [PATCH 11/11] Fixed exit condition for RxQueue. --- src/crypto/rx/RxQueue.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/crypto/rx/RxQueue.cpp b/src/crypto/rx/RxQueue.cpp index 48a1f9797..7eb3da362 100644 --- a/src/crypto/rx/RxQueue.cpp +++ b/src/crypto/rx/RxQueue.cpp @@ -132,15 +132,15 @@ bool xmrig::RxQueue::isReadyUnsafe(const Job &job) const void xmrig::RxQueue::backgroundInit() { - while (true) { + while (m_state != STATE_SHUTDOWN) { std::unique_lock lock(m_mutex); if (m_state == STATE_IDLE) { m_cv.wait(lock, [this]{ return m_state != STATE_IDLE; }); } - if (m_state == STATE_SHUTDOWN) { - break; + if (m_state != STATE_PENDING) { + continue; } const auto item = m_queue.back(); @@ -159,7 +159,8 @@ void xmrig::RxQueue::backgroundInit() m_storage->init(item.seed, item.threads, item.hugePages); lock = std::move(std::unique_lock(m_mutex)); - if (!m_queue.empty()) { + + if (m_state == STATE_SHUTDOWN || !m_queue.empty()) { continue; }