From 48e975b28226514b12a9cb00356e60acfd977794 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Sun, 9 Jul 2017 20:09:41 +0100 Subject: [PATCH 1/7] HWLOC allocation algorithm cleanup --- autoAdjustHwloc.hpp | 246 ++++++++++++++++++++++---------------------- 1 file changed, 122 insertions(+), 124 deletions(-) diff --git a/autoAdjustHwloc.hpp b/autoAdjustHwloc.hpp index 7296207..936eff3 100644 --- a/autoAdjustHwloc.hpp +++ b/autoAdjustHwloc.hpp @@ -2,6 +2,7 @@ #include "console.h" #include +#include #ifdef _WIN32 #include @@ -23,154 +24,151 @@ public: printer::inst()->print_str("The miner evaluates your system and prints a suggestion for the section `cpu_threads_conf` to the terminal.\n"); printer::inst()->print_str("The values are not optimal, please try to tweak the values based on notes in config.txt.\n"); printer::inst()->print_str("Please copy & paste the block within the asterisks to your config.\n\n"); - printer::inst()->print_str("\n**************** Copy&Paste BEGIN ****************\n\n"); - printer::inst()->print_str("\"cpu_threads_conf\" :\n[\n"); - int depth; hwloc_topology_t topology; - hwloc_obj_t socket; - - hwloc_topology_init(&topology); hwloc_topology_load(topology); - depth = hwloc_get_type_depth(topology, HWLOC_OBJ_SOCKET); - if (depth == HWLOC_TYPE_DEPTH_UNKNOWN) + try { - printf("*** The number of sockets is unknown\n"); - } + std::vector tlcs; + tlcs.reserve(16); + results.reserve(16); - for (int i = 0; i < hwloc_get_nbobjs_by_depth(topology, depth); i++) - { - socket = hwloc_get_obj_by_depth(topology, depth, i); + findChildrenByType(hwloc_get_root_obj(topology), HWLOC_OBJ_CACHE, + [&tlcs](hwloc_obj_t found) { tlcs.emplace_back(found); } ); - // search cacheprinter::inst()->print_str("\n**************** Copy&Paste ****************\n\n"); - for (int j = 0; j < socket->arity; j++) + if(tlcs.size() == 0) + throw(std::runtime_error("The CPU doesn't seem to have a cache.")); + + for(hwloc_obj_t obj : tlcs) + proccessTopLevelCache(obj); + + printer::inst()->print_str("\n**************** Copy&Paste BEGIN ****************\n\n"); + printer::inst()->print_str("\"cpu_threads_conf\" :\n[\n"); + + for(uint32_t id : results) { - hwloc_obj_t nextLvl = socket->children[j]; - findCache(topology, nextLvl); + char str[128]; + snprintf(str, sizeof(str), " { \"low_power_mode\" : %s, \"no_prefetch\" : true, \"affine_to_cpu\" : %u },\n", + (id & 0x8000000) != 0 ? "true" : "false", id & 0x7FFFFFF); + printer::inst()->print_str(str); } + + printer::inst()->print_str("],\n\n**************** Copy&Paste END ****************\n"); + } + catch(const std::runtime_error& err) + { + printer::inst()->print_msg(L0, "Autoconf FAILED: %s", err.what()); + printer::inst()->print_str("\nPrinting config for a single thread. Please try to add new ones until the hashrate slows down.\n"); + printer::inst()->print_str("\n**************** FAILURE Copy&Paste BEGIN ****************\n\n"); + printer::inst()->print_str("\"cpu_threads_conf\" :\n[\n"); + printer::inst()->print_str(" { \"low_power_mode\" : false, \"no_prefetch\" : true, \"affine_to_cpu\" : false },\n"); + printer::inst()->print_str("],\n\n**************** FAILURE Copy&Paste END ****************\n"); } /* Destroy topology object. */ hwloc_topology_destroy(topology); - - printer::inst()->print_str("],\n\n**************** Copy&Paste END ****************\n"); } private: + static constexpr size_t hashSize = 2 * 1024 * 1024; + std::vector results; - inline void getConfig(hwloc_topology_t topology, hwloc_obj_t obj, size_t& numHashes, int& numCachesLeft) + template + inline void findChildrenByType(hwloc_obj_t obj, hwloc_obj_type_t type, func lambda) { - if (obj->type == HWLOC_OBJ_CORE) + for(size_t i=0; i < obj->arity; i++) { - if (obj) - { - hwloc_cpuset_t cpuset; - /* Get a copy of its cpuset that we may modify. */ - cpuset = hwloc_bitmap_dup(obj->cpuset); - size_t allcpu = hwloc_bitmap_to_ulong(cpuset); - /* Get only one logical processor (in case the core is - SMT/hyperthreaded). */ - hwloc_bitmap_singlify(cpuset); - - - int firstNativeCore = hwloc_bitmap_first(cpuset); - - int nativeCores = hwloc_bitmap_weight(cpuset); - int numPus = obj->arity; - for (int i = 0; i < numPus && numHashes != 0 && firstNativeCore != -1; i++) - { - hwloc_obj_t pu = obj->children[i]; - // only use native pu's - if (pu->type == HWLOC_OBJ_PU && hwloc_bitmap_isset( cpuset, i + firstNativeCore )) - { - // if no cache is available we give each native core a hash - int numUnit = numCachesLeft != 0 ? numCachesLeft : nativeCores; - - // two hashes per native pu if number of hashes if larger than compute units - int power = numHashes > numUnit ? 2 : 1; - char strbuf[256]; - - snprintf(strbuf, sizeof(strbuf), " { \"low_power_mode\" : %s, \"no_prefetch\" : true, \"affine_to_cpu\" : %u },\n", - power == 2 ? "true" : "false", pu->os_index); - printer::inst()->print_str(strbuf); - - // update number of free hashes - numHashes -= power; - - // one cache is filled with hashes - if (numCachesLeft != 0) numCachesLeft--; - } - } - } - } - else - { - for (int i = 0; i < obj->arity; i++) - getConfig(topology, obj->children[i], numHashes, numCachesLeft); + if(obj->children[i]->type == type) + lambda(obj->children[i]); + else + findChildrenByType(obj->children[i], type, lambda); } } - inline void findCache(hwloc_topology_t topology, hwloc_obj_t obj) + inline bool isCacheInclusive(hwloc_obj_t obj) { - if (obj->type == HWLOC_OBJ_CACHE) - { - size_t cacheSize = obj->attr->cache.size; - size_t numHashL3 = ( cacheSize + m_scratchPadMemSize/ 2llu ) / m_scratchPadMemSize; - - /* check cache is exclusive or inclusive */ - const char* value = hwloc_obj_get_info_by_name(obj, "Inclusive"); - - - bool doL3 = true; - if (value == NULL || value[0] != 49 || cacheSize == 0) - { - size_t numHashes = 0; - int numL2 = obj->arity; - for (int k = 0; k < numL2; k++) - { - hwloc_obj_t l3Cache = obj->children[k]; - size_t l2Cache = 0; - - if (obj->type == HWLOC_OBJ_CACHE) - l2Cache = l3Cache->attr->cache.size; - else - break; - - if (l2Cache < m_scratchPadMemSize) - { - // we need to start from L3 - break; - } - - // start from L2 - - /* if more hashes available than objects in the current depth of the topology - * than divide with round down else round up - */ - int extraHash = numHashL3 > numL2 ? numHashL3 / numL2 : (numHashL3 + numL2 - 1) / numL2; - numHashL3 -= extraHash; - if (numHashL3 < 0) - numHashL3 = 0; - numHashes += extraHash; - //add L2 hashes - numHashes += ( l2Cache + m_scratchPadMemSize / 2llu ) / m_scratchPadMemSize; - int numCachesLeft = numL2; - getConfig(topology, l3Cache, numHashes, numCachesLeft); - doL3 = false; - } - } - if (doL3) - { - int numCachesLeft = obj->arity; - getConfig(topology, obj, numHashL3, numCachesLeft); - } - } - else - for (int j = 0; j < obj->arity; j++) - findCache(topology, obj->children[j]); + const char* value = hwloc_obj_get_info_by_name(obj, "Inclusive"); + return value == nullptr || value[0] == '1'; } - static constexpr size_t m_scratchPadMemSize = ( 2llu * 1024llu * 1024llu ); + // Top level cache isn't shared with other cores on the same package + // This will usually be 1 x L3, but can be 2 x L2 per package + void proccessTopLevelCache(hwloc_obj_t obj) + { + if(obj->attr == nullptr) + throw(std::runtime_error("Cache object hasn't got attributes.")); + + size_t PUs = 0; + findChildrenByType(obj, HWLOC_OBJ_PU, [&PUs](hwloc_obj_t found) { PUs++; } ); + + //Strange case, but we will handle it silently, surely there must be one PU somewhere? + if(PUs == 0) + return; + + if(obj->attr->cache.size == 0) + { + //We will always have one child if PUs > 0 + if(obj->children[0]->type != HWLOC_OBJ_CACHE) + throw(std::runtime_error("The CPU doesn't seem to have a cache.")); + + //Try our luck with lower level caches + for(size_t i=0; i < obj->arity; i++) + proccessTopLevelCache(obj->children[i]); + return; + } + + + size_t cacheSize = obj->attr->cache.size; + if(!isCacheInclusive(obj)) + { + for(size_t i=0; i < obj->arity; i++) + { + //If L2 is exclusive and greater or equal to 2MB add room for one more hash + if(obj->type == HWLOC_OBJ_CACHE && obj->attr != nullptr && obj->attr->cache.size >= hashSize) + cacheSize += hashSize; + } + } + + std::vector cores; + cores.reserve(16); + findChildrenByType(obj, HWLOC_OBJ_CORE, [&cores](hwloc_obj_t found) { cores.emplace_back(found); } ); + + size_t cacheHashes = (obj->attr->cache.size + hashSize - 1) / hashSize; + + //Firstly allocate PU 0 of every CORE, then PU 1 etc. + size_t pu_id = 0; + while(cacheHashes > 0 && PUs > 0) + { + bool allocated_pu = false; + for(hwloc_obj_t core : cores) + { + if(core->arity <= pu_id || core->children[pu_id]->type != HWLOC_OBJ_PU) + continue; + + size_t os_id = core->children[pu_id]->os_index; + + if(cacheHashes > PUs) + { + cacheHashes -= 2; + os_id |= 0x8000000; //double hash marker bit + } + else + cacheHashes--; + PUs--; + + allocated_pu = true; + results.emplace_back(os_id); + + if(cacheHashes == 0) + break; + } + + if(!allocated_pu) + throw(std::runtime_error("Failed to allocate a PU.")); + + pu_id++; + } + } }; From 5c174969a7d269e5ca8ab919a58cb3e11786c274 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Sun, 9 Jul 2017 21:17:17 +0100 Subject: [PATCH 2/7] Minor slipup --- autoAdjustHwloc.hpp | 4 ++-- xmr-stak-cpu.cbp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/autoAdjustHwloc.hpp b/autoAdjustHwloc.hpp index 936eff3..8e4acd4 100644 --- a/autoAdjustHwloc.hpp +++ b/autoAdjustHwloc.hpp @@ -119,14 +119,14 @@ private: return; } - size_t cacheSize = obj->attr->cache.size; if(!isCacheInclusive(obj)) { for(size_t i=0; i < obj->arity; i++) { + hwloc_obj_t l2obj = obj->children[i]; //If L2 is exclusive and greater or equal to 2MB add room for one more hash - if(obj->type == HWLOC_OBJ_CACHE && obj->attr != nullptr && obj->attr->cache.size >= hashSize) + if(l2obj->type == HWLOC_OBJ_CACHE && l2obj->attr != nullptr && l2obj->attr->cache.size >= hashSize) cacheSize += hashSize; } } diff --git a/xmr-stak-cpu.cbp b/xmr-stak-cpu.cbp index b9d6e9d..c2f3774 100644 --- a/xmr-stak-cpu.cbp +++ b/xmr-stak-cpu.cbp @@ -70,6 +70,7 @@ + @@ -108,6 +109,7 @@ + From dcaa25fd7044667f8fab11c5b34eac2d5a027ff7 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Sun, 9 Jul 2017 22:28:52 +0100 Subject: [PATCH 3/7] Fix exclusive cache detection --- autoAdjustHwloc.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autoAdjustHwloc.hpp b/autoAdjustHwloc.hpp index 8e4acd4..bb7d18c 100644 --- a/autoAdjustHwloc.hpp +++ b/autoAdjustHwloc.hpp @@ -87,10 +87,10 @@ private: } } - inline bool isCacheInclusive(hwloc_obj_t obj) + inline bool isCacheExclusive(hwloc_obj_t obj) { const char* value = hwloc_obj_get_info_by_name(obj, "Inclusive"); - return value == nullptr || value[0] == '1'; + return value == nullptr || value[0] != '1'; } // Top level cache isn't shared with other cores on the same package @@ -120,7 +120,7 @@ private: } size_t cacheSize = obj->attr->cache.size; - if(!isCacheInclusive(obj)) + if(isCacheExclusive(obj)) { for(size_t i=0; i < obj->arity; i++) { From 69f95e4e2d29c0dffa1e654aa10b25ac74e06d83 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Sun, 9 Jul 2017 23:18:56 +0100 Subject: [PATCH 4/7] Commit hwlocMemory.hpp fixes on top of this branch (cosmetic, no logic changes) --- hwlocMemory.hpp | 14 ++++++++------ minethd.cpp | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hwlocMemory.hpp b/hwlocMemory.hpp index b9972ce..f471951 100644 --- a/hwlocMemory.hpp +++ b/hwlocMemory.hpp @@ -13,40 +13,42 @@ * * @param puId core id */ -void bindMemoryToNUMANode( int puId ) +void bindMemoryToNUMANode( size_t puId ) { int depth; hwloc_topology_t topology; - hwloc_obj_t obj; hwloc_topology_init(&topology); hwloc_topology_load(topology); depth = hwloc_get_type_depth(topology, HWLOC_OBJ_PU); - for( int i = 0; + for( size_t i = 0; i < hwloc_get_nbobjs_by_depth(topology, depth); i++ ) { hwloc_obj_t pu = hwloc_get_obj_by_depth(topology, depth, i); if( pu->os_index == puId ) + { if( 0 > hwloc_set_membind_nodeset( topology, pu->nodeset, HWLOC_MEMBIND_BIND, - HWLOC_MEMBIND_THREAD) - ) + HWLOC_MEMBIND_THREAD)) + { printer::inst()->print_msg(L0, "hwloc: can't bind memory"); + } else { printer::inst()->print_msg(L0, "hwloc: memory pinned"); break; } + } } } #else -void bindMemoryToNUMANode( int ) +void bindMemoryToNUMANode( size_t ) { } diff --git a/minethd.cpp b/minethd.cpp index 6fbbb3c..fd83e82 100644 --- a/minethd.cpp +++ b/minethd.cpp @@ -373,7 +373,7 @@ minethd::cn_hash_fun minethd::func_selector(bool bHaveAes, bool bNoPrefetch) void minethd::work_main() { // pin memory to NUMA node - bindMemoryToNUMANode(this->affinity); + bindMemoryToNUMANode(affinity); cn_hash_fun hash_fun; cryptonight_ctx* ctx; @@ -463,7 +463,7 @@ minethd::cn_hash_fun_dbl minethd::func_dbl_selector(bool bHaveAes, bool bNoPrefe void minethd::double_work_main() { // pin memory to NUMA node - bindMemoryToNUMANode(this->affinity); + bindMemoryToNUMANode(affinity); cn_hash_fun_dbl hash_fun; cryptonight_ctx* ctx0; From 71c40a49c32e77551a325f371a835904d626ca2e Mon Sep 17 00:00:00 2001 From: psychocrypt Date: Mon, 10 Jul 2017 21:43:21 +0200 Subject: [PATCH 5/7] fix - fix exclusive cache detection - fix usage of wrong cache size --- autoAdjustHwloc.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autoAdjustHwloc.hpp b/autoAdjustHwloc.hpp index bb7d18c..c76cd5f 100644 --- a/autoAdjustHwloc.hpp +++ b/autoAdjustHwloc.hpp @@ -90,7 +90,7 @@ private: inline bool isCacheExclusive(hwloc_obj_t obj) { const char* value = hwloc_obj_get_info_by_name(obj, "Inclusive"); - return value == nullptr || value[0] != '1'; + return value != nullptr && value[0] == '1'; } // Top level cache isn't shared with other cores on the same package @@ -120,7 +120,7 @@ private: } size_t cacheSize = obj->attr->cache.size; - if(isCacheExclusive(obj)) + if(!isCacheExclusive(obj)) { for(size_t i=0; i < obj->arity; i++) { @@ -135,7 +135,7 @@ private: cores.reserve(16); findChildrenByType(obj, HWLOC_OBJ_CORE, [&cores](hwloc_obj_t found) { cores.emplace_back(found); } ); - size_t cacheHashes = (obj->attr->cache.size + hashSize - 1) / hashSize; + size_t cacheHashes = (cacheSize + hashSize - 1) / hashSize; //Firstly allocate PU 0 of every CORE, then PU 1 etc. size_t pu_id = 0; From d3150bf0a5180f6f36ecbb42d416994cc6fac88d Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Mon, 10 Jul 2017 21:25:11 +0100 Subject: [PATCH 6/7] Make the code portable to hwloc 2.x --- autoAdjustHwloc.hpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/autoAdjustHwloc.hpp b/autoAdjustHwloc.hpp index c76cd5f..5e010a2 100644 --- a/autoAdjustHwloc.hpp +++ b/autoAdjustHwloc.hpp @@ -35,7 +35,7 @@ public: tlcs.reserve(16); results.reserve(16); - findChildrenByType(hwloc_get_root_obj(topology), HWLOC_OBJ_CACHE, + findChildrenCaches(hwloc_get_root_obj(topology), [&tlcs](hwloc_obj_t found) { tlcs.emplace_back(found); } ); if(tlcs.size() == 0) @@ -87,6 +87,27 @@ private: } } + inline bool isCacheObject(hwloc_obj_t obj) + { +#if HWLOC_API_VERSION >= 0x20000 + return hwloc_obj_type_is_cache(obj->type); +#else + return obj->type == HWLOC_OBJ_CACHE; +#endif // HWLOC_API_VERSION + } + + template + inline void findChildrenCaches(hwloc_obj_t obj, func lambda) + { + for(size_t i=0; i < obj->arity; i++) + { + if(isCacheObject(obj->children[i])) + lambda(obj->children[i]); + else + findChildrenCaches(obj->children[i], lambda); + } + } + inline bool isCacheExclusive(hwloc_obj_t obj) { const char* value = hwloc_obj_get_info_by_name(obj, "Inclusive"); @@ -110,7 +131,7 @@ private: if(obj->attr->cache.size == 0) { //We will always have one child if PUs > 0 - if(obj->children[0]->type != HWLOC_OBJ_CACHE) + if(!isCacheObject(obj->children[0])) throw(std::runtime_error("The CPU doesn't seem to have a cache.")); //Try our luck with lower level caches @@ -126,7 +147,7 @@ private: { hwloc_obj_t l2obj = obj->children[i]; //If L2 is exclusive and greater or equal to 2MB add room for one more hash - if(l2obj->type == HWLOC_OBJ_CACHE && l2obj->attr != nullptr && l2obj->attr->cache.size >= hashSize) + if(isCacheObject(l2obj) && l2obj->attr != nullptr && l2obj->attr->cache.size >= hashSize) cacheSize += hashSize; } } From 90db7e9eb9b04c5b8bdabfb24d94ffd97041821e Mon Sep 17 00:00:00 2001 From: psychocrypt Date: Wed, 12 Jul 2017 21:02:23 +0200 Subject: [PATCH 7/7] fix wrong cache method - fix wrong implemented method `isCacheExclusive()` - round up number of hashes if >= 1 MiB cache is available --- autoAdjustHwloc.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autoAdjustHwloc.hpp b/autoAdjustHwloc.hpp index 5e010a2..f6711fb 100644 --- a/autoAdjustHwloc.hpp +++ b/autoAdjustHwloc.hpp @@ -111,7 +111,7 @@ private: inline bool isCacheExclusive(hwloc_obj_t obj) { const char* value = hwloc_obj_get_info_by_name(obj, "Inclusive"); - return value != nullptr && value[0] == '1'; + return value == nullptr || value[0] != '1'; } // Top level cache isn't shared with other cores on the same package @@ -141,7 +141,7 @@ private: } size_t cacheSize = obj->attr->cache.size; - if(!isCacheExclusive(obj)) + if(isCacheExclusive(obj)) { for(size_t i=0; i < obj->arity; i++) { @@ -156,7 +156,7 @@ private: cores.reserve(16); findChildrenByType(obj, HWLOC_OBJ_CORE, [&cores](hwloc_obj_t found) { cores.emplace_back(found); } ); - size_t cacheHashes = (cacheSize + hashSize - 1) / hashSize; + size_t cacheHashes = (cacheSize + hashSize/2u - 1) / hashSize; //Firstly allocate PU 0 of every CORE, then PU 1 etc. size_t pu_id = 0;