/* XMRig * Copyright 2018-2020 SChernykh * Copyright 2016-2020 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "crypto/kawpow/KPCache.h" #include "3rdparty/libethash/data_sizes.h" #include "3rdparty/libethash/ethash_internal.h" #include "3rdparty/libethash/ethash.h" #include "base/io/log/Log.h" #include "base/io/log/Tags.h" #include "base/tools/Chrono.h" #include "crypto/common/VirtualMemory.h" namespace xmrig { std::mutex KPCache::s_cacheMutex; KPCache KPCache::s_cache; KPCache::KPCache() { } KPCache::~KPCache() { delete m_memory; } bool KPCache::init(uint32_t epoch) { if (epoch >= sizeof(cache_sizes) / sizeof(cache_sizes[0])) { return false; } if (m_epoch == epoch) { return true; } const uint64_t start_ms = Chrono::steadyMSecs(); const size_t size = cache_sizes[epoch]; if (!m_memory || m_memory->size() < size) { delete m_memory; m_memory = new VirtualMemory(size, false, false, false); } const ethash_h256_t seedhash = ethash_get_seedhash(epoch); ethash_compute_cache_nodes(m_memory->raw(), size, &seedhash); ethash_light cache; cache.cache = m_memory->raw(); cache.cache_size = size; cache.num_parent_nodes = cache.cache_size / sizeof(node); calculate_fast_mod_data(cache.num_parent_nodes, cache.reciprocal, cache.increment, cache.shift); const uint64_t cache_nodes = (size + sizeof(node) * 4 - 1) / sizeof(node); m_DAGCache.resize(cache_nodes * (sizeof(node) / sizeof(uint32_t))); // Init DAG cache { const uint64_t n = std::max(std::thread::hardware_concurrency(), 1U); std::vector threads; threads.reserve(n); for (uint64_t i = 0; i < n; ++i) { const uint32_t a = (cache_nodes * i) / n; const uint32_t b = (cache_nodes * (i + 1)) / n; threads.emplace_back([this, a, b, &cache]() { uint32_t j = a; for (; j + 4 <= b; j += 4) ethash_calculate_dag_item4_opt(((node*)m_DAGCache.data()) + j, j, num_dataset_parents, &cache); for (; j < b; ++j) ethash_calculate_dag_item_opt(((node*)m_DAGCache.data()) + j, j, num_dataset_parents, &cache); }); } for (auto& t : threads) { t.join(); } } m_size = size; m_epoch = epoch; LOG_INFO("%s " YELLOW("KawPow") " light cache for epoch " WHITE_BOLD("%u") " calculated " BLACK_BOLD("(%" PRIu64 "ms)"), Tags::miner(), epoch, Chrono::steadyMSecs() - start_ms); return true; } void* KPCache::data() const { return m_memory ? m_memory->raw() : nullptr; } static inline uint32_t clz(uint32_t a) { #ifdef _MSC_VER unsigned long index; _BitScanReverse(&index, a); return 31 - index; #else return __builtin_clz(a); #endif } uint64_t KPCache::cache_size(uint32_t epoch) { if (epoch >= sizeof(cache_sizes) / sizeof(cache_sizes[0])) { return 0; } return cache_sizes[epoch]; } uint64_t KPCache::dag_size(uint32_t epoch) { if (epoch >= sizeof(dag_sizes) / sizeof(dag_sizes[0])) { return 0; } return dag_sizes[epoch]; } void KPCache::calculate_fast_mod_data(uint32_t divisor, uint32_t& reciprocal, uint32_t& increment, uint32_t& shift) { if ((divisor & (divisor - 1)) == 0) { reciprocal = 1; increment = 0; shift = 31U - clz(divisor); } else { shift = 63U - clz(divisor); const uint64_t N = 1ULL << shift; const uint64_t q = N / divisor; const uint64_t r = N - q * divisor; if (r * 2 < divisor) { reciprocal = static_cast(q); increment = 1; } else { reciprocal = static_cast(q + 1); increment = 0; } } } } // namespace xmrig