diff --git a/src/backend/common/Threads.h b/src/backend/common/Threads.h index afae184c..f73b22a0 100644 --- a/src/backend/common/Threads.h +++ b/src/backend/common/Threads.h @@ -44,6 +44,7 @@ class Threads public: inline bool has(const char *profile) const { return m_profiles.count(profile) > 0; } inline bool isDisabled(const Algorithm &algo) const { return m_disabled.count(algo) > 0; } + inline bool isEmpty() const { return m_profiles.empty(); } inline bool isExist(const Algorithm &algo) const { return isDisabled(algo) || m_aliases.count(algo) > 0 || has(algo.shortName()); } inline const T &get(const Algorithm &algo, bool strict = false) const { return get(profileName(algo, strict)); } inline void disable(const Algorithm &algo) { m_disabled.insert(algo); } diff --git a/src/backend/cpu/CpuConfig.cpp b/src/backend/cpu/CpuConfig.cpp index 7ea6a38b..8a8a76c6 100644 --- a/src/backend/cpu/CpuConfig.cpp +++ b/src/backend/cpu/CpuConfig.cpp @@ -35,6 +35,7 @@ static const char *kCn = "cn"; static const char *kEnabled = "enabled"; static const char *kHugePages = "huge-pages"; static const char *kHwAes = "hw-aes"; +static const char *kMaxThreadsHint = "max-threads-hint"; static const char *kPriority = "priority"; #ifdef XMRIG_FEATURE_ASM @@ -72,11 +73,6 @@ extern template class Threads; } -xmrig::CpuConfig::CpuConfig() -{ -} - - bool xmrig::CpuConfig::isHwAES() const { return (m_aes == AES_AUTO ? (Cpu::info()->hasAES() ? AES_HW : AES_SOFT) : m_aes) == AES_HW; @@ -95,6 +91,10 @@ rapidjson::Value xmrig::CpuConfig::toJSON(rapidjson::Document &doc) const obj.AddMember(StringRef(kHwAes), m_aes == AES_AUTO ? Value(kNullType) : Value(m_aes == AES_HW), allocator); obj.AddMember(StringRef(kPriority), priority() != -1 ? Value(priority()) : Value(kNullType), allocator); + if (m_threads.isEmpty()) { + obj.AddMember(StringRef(kMaxThreadsHint), m_limit, allocator); + } + # ifdef XMRIG_FEATURE_ASM obj.AddMember(StringRef(kAsm), m_assembly.toJSON(), allocator); # endif @@ -131,8 +131,9 @@ std::vector xmrig::CpuConfig::get(const Miner *miner, cons void xmrig::CpuConfig::read(const rapidjson::Value &value, uint32_t version) { if (value.IsObject()) { - m_enabled = Json::getBool(value, kEnabled, m_enabled); - m_hugePages = Json::getBool(value, kHugePages, m_hugePages); + m_enabled = Json::getBool(value, kEnabled, m_enabled); + m_hugePages = Json::getBool(value, kHugePages, m_hugePages); + m_limit = Json::getUint(value, kMaxThreadsHint, m_limit); setAesMode(Json::getValue(value, kHwAes)); setPriority(Json::getInt(value, kPriority, -1)); @@ -168,28 +169,28 @@ void xmrig::CpuConfig::generate() ICpuInfo *cpu = Cpu::info(); m_threads.disable(Algorithm::CN_0); - m_threads.move(kCn, cpu->threads(Algorithm::CN_0)); + m_threads.move(kCn, cpu->threads(Algorithm::CN_0, m_limit)); # ifdef XMRIG_ALGO_CN_GPU - m_threads.move(kCnGPU, cpu->threads(Algorithm::CN_GPU)); + m_threads.move(kCnGPU, cpu->threads(Algorithm::CN_GPU, m_limit)); # endif # ifdef XMRIG_ALGO_CN_LITE m_threads.disable(Algorithm::CN_LITE_0); - m_threads.move(kCnLite, cpu->threads(Algorithm::CN_LITE_1)); + m_threads.move(kCnLite, cpu->threads(Algorithm::CN_LITE_1, m_limit)); # endif # ifdef XMRIG_ALGO_CN_HEAVY - m_threads.move(kCnHeavy, cpu->threads(Algorithm::CN_HEAVY_0)); + m_threads.move(kCnHeavy, cpu->threads(Algorithm::CN_HEAVY_0, m_limit)); # endif # ifdef XMRIG_ALGO_CN_PICO - m_threads.move(kCnPico, cpu->threads(Algorithm::CN_PICO_0)); + m_threads.move(kCnPico, cpu->threads(Algorithm::CN_PICO_0, m_limit)); # endif # ifdef XMRIG_ALGO_RANDOMX - m_threads.move(kRx, cpu->threads(Algorithm::RX_0)); - m_threads.move(kRxWOW, cpu->threads(Algorithm::RX_WOW)); + m_threads.move(kRx, cpu->threads(Algorithm::RX_0, m_limit)); + m_threads.move(kRxWOW, cpu->threads(Algorithm::RX_WOW, m_limit)); # endif generateArgon2(); @@ -199,7 +200,7 @@ void xmrig::CpuConfig::generate() void xmrig::CpuConfig::generateArgon2() { # ifdef XMRIG_ALGO_ARGON2 - m_threads.move(kArgon2, Cpu::info()->threads(Algorithm::AR2_CHUKWA)); + m_threads.move(kArgon2, Cpu::info()->threads(Algorithm::AR2_CHUKWA, m_limit)); # endif } diff --git a/src/backend/cpu/CpuConfig.h b/src/backend/cpu/CpuConfig.h index 67010eea..27075425 100644 --- a/src/backend/cpu/CpuConfig.h +++ b/src/backend/cpu/CpuConfig.h @@ -44,7 +44,7 @@ public: AES_SOFT }; - CpuConfig(); + CpuConfig() = default; bool isHwAES() const; rapidjson::Value toJSON(rapidjson::Document &doc) const; @@ -74,6 +74,7 @@ private: int m_priority = -1; String m_argon2Impl; Threads m_threads; + uint32_t m_limit = 100; }; diff --git a/src/backend/cpu/interfaces/ICpuInfo.h b/src/backend/cpu/interfaces/ICpuInfo.h index 9bc3b11a..20e72391 100644 --- a/src/backend/cpu/interfaces/ICpuInfo.h +++ b/src/backend/cpu/interfaces/ICpuInfo.h @@ -45,18 +45,18 @@ public: inline constexpr static bool isX64() { return false; } # endif - virtual Assembly::Id assembly() const = 0; - virtual bool hasAES() const = 0; - virtual bool hasAVX2() const = 0; - virtual const char *backend() const = 0; - virtual const char *brand() const = 0; - virtual CpuThreads threads(const Algorithm &algorithm) const = 0; - virtual size_t cores() const = 0; - virtual size_t L2() const = 0; - virtual size_t L3() const = 0; - virtual size_t nodes() const = 0; - virtual size_t packages() const = 0; - virtual size_t threads() const = 0; + virtual Assembly::Id assembly() const = 0; + virtual bool hasAES() const = 0; + virtual bool hasAVX2() const = 0; + virtual const char *backend() const = 0; + virtual const char *brand() const = 0; + virtual CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const = 0; + virtual size_t cores() const = 0; + virtual size_t L2() const = 0; + virtual size_t L3() const = 0; + virtual size_t nodes() const = 0; + virtual size_t packages() const = 0; + virtual size_t threads() const = 0; }; diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.cpp b/src/backend/cpu/platform/AdvancedCpuInfo.cpp index 26798895..5cae55e2 100644 --- a/src/backend/cpu/platform/AdvancedCpuInfo.cpp +++ b/src/backend/cpu/platform/AdvancedCpuInfo.cpp @@ -23,10 +23,10 @@ */ #include -#include -#include -#include -#include +#include +#include +#include +#include #include "3rdparty/libcpuid/libcpuid.h" @@ -109,7 +109,7 @@ xmrig::AdvancedCpuInfo::AdvancedCpuInfo() : } -xmrig::CpuThreads xmrig::AdvancedCpuInfo::threads(const Algorithm &algorithm) const +xmrig::CpuThreads xmrig::AdvancedCpuInfo::threads(const Algorithm &algorithm, uint32_t limit) const { if (threads() == 1) { return 1; @@ -153,5 +153,12 @@ xmrig::CpuThreads xmrig::AdvancedCpuInfo::threads(const Algorithm &algorithm) co } # endif - return CpuThreads(std::max(std::min(count, threads()), 1), intensity); + if (limit > 0 && limit < 100) { + count = std::min(count, static_cast(round(threads() * (limit / 100.0)))); + } + else { + count = std::min(count, threads()); + } + + return CpuThreads(std::max(count, 1), intensity); } diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.h b/src/backend/cpu/platform/AdvancedCpuInfo.h index 51b84c9f..e2909a91 100644 --- a/src/backend/cpu/platform/AdvancedCpuInfo.h +++ b/src/backend/cpu/platform/AdvancedCpuInfo.h @@ -38,7 +38,7 @@ public: AdvancedCpuInfo(); protected: - CpuThreads threads(const Algorithm &algorithm) const override; + CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const override; inline Assembly::Id assembly() const override { return m_assembly; } inline bool hasAES() const override { return m_aes; } diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp index 15ac8f40..db3741ee 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo.cpp @@ -179,7 +179,7 @@ const char *xmrig::BasicCpuInfo::backend() const } -xmrig::CpuThreads xmrig::BasicCpuInfo::threads(const Algorithm &algorithm) const +xmrig::CpuThreads xmrig::BasicCpuInfo::threads(const Algorithm &algorithm, uint32_t limit) const { const size_t count = std::thread::hardware_concurrency(); diff --git a/src/backend/cpu/platform/BasicCpuInfo.h b/src/backend/cpu/platform/BasicCpuInfo.h index 6cf25714..4c68c5f8 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.h +++ b/src/backend/cpu/platform/BasicCpuInfo.h @@ -39,7 +39,7 @@ public: protected: const char *backend() const override; - CpuThreads threads(const Algorithm &algorithm) const override; + CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const override; inline Assembly::Id assembly() const override { return m_assembly; } inline bool hasAES() const override { return m_aes; } diff --git a/src/backend/cpu/platform/HwlocCpuInfo.cpp b/src/backend/cpu/platform/HwlocCpuInfo.cpp index 9e503742..e87faf2d 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.cpp +++ b/src/backend/cpu/platform/HwlocCpuInfo.cpp @@ -29,6 +29,7 @@ #include +#include #include @@ -127,9 +128,7 @@ static inline bool isCacheExclusive(hwloc_obj_t obj) } // namespace xmrig -xmrig::HwlocCpuInfo::HwlocCpuInfo() : BasicCpuInfo(), - m_backend(), - m_cache() +xmrig::HwlocCpuInfo::HwlocCpuInfo() { m_threads = 0; @@ -149,7 +148,7 @@ xmrig::HwlocCpuInfo::HwlocCpuInfo() : BasicCpuInfo(), # endif const std::vector packages = findByType(hwloc_get_root_obj(m_topology), HWLOC_OBJ_PACKAGE); - if (packages.size()) { + if (!packages.empty()) { const char *value = hwloc_obj_get_info_by_name(packages[0], "CPUModel"); if (value) { strncpy(m_brand, value, 64); @@ -202,10 +201,10 @@ xmrig::HwlocCpuInfo::~HwlocCpuInfo() } -xmrig::CpuThreads xmrig::HwlocCpuInfo::threads(const Algorithm &algorithm) const +xmrig::CpuThreads xmrig::HwlocCpuInfo::threads(const Algorithm &algorithm, uint32_t limit) const { if (L2() == 0 && L3() == 0) { - return BasicCpuInfo::threads(algorithm); + return BasicCpuInfo::threads(algorithm, limit); } const unsigned depth = L3() > 0 ? 3 : 2; @@ -218,21 +217,37 @@ xmrig::CpuThreads xmrig::HwlocCpuInfo::threads(const Algorithm &algorithm) const findCache(hwloc_get_root_obj(m_topology), depth, depth, [&caches](hwloc_obj_t found) { caches.emplace_back(found); }); - for (hwloc_obj_t cache : caches) { - processTopLevelCache(cache, algorithm, threads); + if (limit > 0 && limit < 100 && !caches.empty()) { + const double maxTotalThreads = round(m_threads * (limit / 100.0)); + const auto maxPerCache = std::max(static_cast(round(maxTotalThreads / caches.size())), 1); + int remaining = std::max(static_cast(maxTotalThreads), 1); + + for (hwloc_obj_t cache : caches) { + processTopLevelCache(cache, algorithm, threads, std::min(maxPerCache, remaining)); + + remaining -= maxPerCache; + if (remaining <= 0) { + break; + } + } + } + else { + for (hwloc_obj_t cache : caches) { + processTopLevelCache(cache, algorithm, threads, 0); + } } if (threads.isEmpty()) { LOG_WARN("hwloc auto configuration for algorithm \"%s\" failed.", algorithm.shortName()); - return BasicCpuInfo::threads(algorithm); + return BasicCpuInfo::threads(algorithm, limit); } return threads; } -void xmrig::HwlocCpuInfo::processTopLevelCache(hwloc_obj_t cache, const Algorithm &algorithm, CpuThreads &threads) const +void xmrig::HwlocCpuInfo::processTopLevelCache(hwloc_obj_t cache, const Algorithm &algorithm, CpuThreads &threads, size_t limit) const { constexpr size_t oneMiB = 1024u * 1024u; @@ -296,6 +311,10 @@ void xmrig::HwlocCpuInfo::processTopLevelCache(hwloc_obj_t cache, const Algorith } # endif + if (limit > 0) { + cacheHashes = std::min(cacheHashes, limit); + } + if (cacheHashes >= PUs) { for (hwloc_obj_t core : cores) { const std::vector units = findByType(core, HWLOC_OBJ_PU); diff --git a/src/backend/cpu/platform/HwlocCpuInfo.h b/src/backend/cpu/platform/HwlocCpuInfo.h index 340864f5..ec4aea2c 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.h +++ b/src/backend/cpu/platform/HwlocCpuInfo.h @@ -27,10 +27,11 @@ #include "backend/cpu/platform/BasicCpuInfo.h" +#include "base/tools/Object.h" -typedef struct hwloc_obj *hwloc_obj_t; -typedef struct hwloc_topology *hwloc_topology_t; +using hwloc_obj_t = struct hwloc_obj *; +using hwloc_topology_t = struct hwloc_topology *; namespace xmrig { @@ -39,6 +40,9 @@ namespace xmrig { class HwlocCpuInfo : public BasicCpuInfo { public: + XMRIG_DISABLE_COPY_MOVE(HwlocCpuInfo) + + enum Feature : uint32_t { SET_THISTHREAD_MEMBIND = 1 }; @@ -51,7 +55,7 @@ public: static inline const std::vector &nodeIndexes() { return m_nodeIndexes; } protected: - CpuThreads threads(const Algorithm &algorithm) const override; + CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const override; inline const char *backend() const override { return m_backend; } inline size_t cores() const override { return m_cores; } @@ -61,17 +65,17 @@ protected: inline size_t packages() const override { return m_packages; } private: - void processTopLevelCache(hwloc_obj_t obj, const Algorithm &algorithm, CpuThreads &threads) const; + 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]; - hwloc_topology_t m_topology; - size_t m_cache[5]; - size_t m_cores = 0; - size_t m_nodes = 0; - size_t m_packages = 0; + char m_backend[20] = { 0 }; + hwloc_topology_t m_topology = nullptr; + size_t m_cache[5] = { 0 }; + size_t m_cores = 0; + size_t m_nodes = 0; + size_t m_packages = 0; }; diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h index 16863fff..8798de5f 100644 --- a/src/base/kernel/interfaces/IConfig.h +++ b/src/base/kernel/interfaces/IConfig.h @@ -88,6 +88,7 @@ public: AssemblyKey = 1015, RandomXInitKey = 1022, RandomXNumaKey = 1023, + CPUMaxThreadsKey = 1026, // xmrig amd OclPlatformKey = 1400, diff --git a/src/core/config/ConfigTransform.cpp b/src/core/config/ConfigTransform.cpp index e7bd383f..3bdbcac5 100644 --- a/src/core/config/ConfigTransform.cpp +++ b/src/core/config/ConfigTransform.cpp @@ -138,6 +138,9 @@ void xmrig::ConfigTransform::transform(rapidjson::Document &doc, int key, const return transformUint64(doc, key, p ? strtoull(p, nullptr, 16) : strtoull(arg, nullptr, 10)); } + case IConfig::CPUMaxThreadsKey: /* --cpu-max-threads-hint */ + return set(doc, kCpu, "max-threads-hint", static_cast(strtol(arg, nullptr, 10))); + # ifdef XMRIG_FEATURE_ASM case IConfig::AssemblyKey: /* --asm */ return set(doc, kCpu, "asm", arg); diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h index c05e84ce..b7f15daa 100644 --- a/src/core/config/Config_platform.h +++ b/src/core/config/Config_platform.h @@ -82,6 +82,8 @@ static const option options[] = { { "userpass", 1, nullptr, IConfig::UserpassKey }, { "rig-id", 1, nullptr, IConfig::RigIdKey }, { "no-cpu", 0, nullptr, IConfig::CPUKey }, + { "max-cpu-usage", 1, nullptr, IConfig::CPUMaxThreadsKey }, + { "cpu-max-threads-hint", 1, nullptr, IConfig::CPUMaxThreadsKey }, # ifdef XMRIG_FEATURE_TLS { "tls", 0, nullptr, IConfig::TlsKey }, { "tls-fingerprint", 1, nullptr, IConfig::FingerprintKey }, diff --git a/src/core/config/usage.h b/src/core/config/usage.h index fc9695a3..0d7cdc5e 100644 --- a/src/core/config/usage.h +++ b/src/core/config/usage.h @@ -76,6 +76,7 @@ static inline const std::string &usage() u += " -v, --av=N algorithm variation, 0 auto select\n"; u += " --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n"; u += " --cpu-priority set process priority (0 idle, 2 normal to 5 highest)\n"; + u += " --cpu-max-threads-hint=N maximum CPU threads count (in percentage) hint for autoconfig\n"; u += " --no-huge-pages disable huge pages support\n"; u += " --asm=ASM ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer\n";