diff options
Diffstat (limited to 'minethd.cpp')
-rw-r--r-- | minethd.cpp | 555 |
1 files changed, 0 insertions, 555 deletions
diff --git a/minethd.cpp b/minethd.cpp deleted file mode 100644 index fac9fb4..0000000 --- a/minethd.cpp +++ /dev/null @@ -1,555 +0,0 @@ -/* - * 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 - * 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 <http://www.gnu.org/licenses/>. - * - * Additional permission under GNU GPL version 3 section 7 - * - * If you modify this Program, or any covered work, by linking or combining - * it with OpenSSL (or a modified version of that library), containing parts - * covered by the terms of OpenSSL License and SSLeay License, the licensors - * of this Program grant you additional permission to convey the resulting work. - * - */ - -#include <assert.h> -#include <cmath> -#include <chrono> -#include <cstring> -#include <thread> -#include <bitset> -#include "console.h" - -#ifdef _WIN32 -#include <windows.h> - -void thd_setaffinity(std::thread::native_handle_type h, uint64_t cpu_id) -{ - SetThreadAffinityMask(h, 1ULL << cpu_id); -} -#else -#include <pthread.h> - -#if defined(__APPLE__) -#include <mach/thread_policy.h> -#include <mach/thread_act.h> -#define SYSCTL_CORE_COUNT "machdep.cpu.core_count" -#elif defined(__FreeBSD__) -#include <pthread_np.h> -#endif - - -void thd_setaffinity(std::thread::native_handle_type h, uint64_t cpu_id) -{ -#if defined(__APPLE__) - thread_port_t mach_thread; - thread_affinity_policy_data_t policy = { static_cast<integer_t>(cpu_id) }; - mach_thread = pthread_mach_thread_np(h); - thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, 1); -#elif defined(__FreeBSD__) - cpuset_t mn; - CPU_ZERO(&mn); - CPU_SET(cpu_id, &mn); - pthread_setaffinity_np(h, sizeof(cpuset_t), &mn); -#else - cpu_set_t mn; - CPU_ZERO(&mn); - CPU_SET(cpu_id, &mn); - pthread_setaffinity_np(h, sizeof(cpu_set_t), &mn); -#endif -} -#endif // _WIN32 - -#include "executor.h" -#include "minethd.h" -#include "jconf.h" -#include "crypto/cryptonight_aesni.h" -#include "hwlocMemory.hpp" - -telemetry::telemetry(size_t iThd) -{ - ppHashCounts = new uint64_t*[iThd]; - ppTimestamps = new uint64_t*[iThd]; - iBucketTop = new uint32_t[iThd]; - - for (size_t i = 0; i < iThd; i++) - { - ppHashCounts[i] = new uint64_t[iBucketSize]; - ppTimestamps[i] = new uint64_t[iBucketSize]; - iBucketTop[i] = 0; - memset(ppHashCounts[0], 0, sizeof(uint64_t) * iBucketSize); - memset(ppTimestamps[0], 0, sizeof(uint64_t) * iBucketSize); - } -} - -double telemetry::calc_telemetry_data(size_t iLastMilisec, size_t iThread) -{ - using namespace std::chrono; - uint64_t iTimeNow = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count(); - - uint64_t iEarliestHashCnt = 0; - uint64_t iEarliestStamp = 0; - uint64_t iLastestStamp = 0; - uint64_t iLastestHashCnt = 0; - bool bHaveFullSet = false; - - //Start at 1, buckettop points to next empty - for (size_t i = 1; i < iBucketSize; i++) - { - size_t idx = (iBucketTop[iThread] - i) & iBucketMask; //overflow expected here - - if (ppTimestamps[iThread][idx] == 0) - break; //That means we don't have the data yet - - if (iLastestStamp == 0) - { - iLastestStamp = ppTimestamps[iThread][idx]; - iLastestHashCnt = ppHashCounts[iThread][idx]; - } - - if (iTimeNow - ppTimestamps[iThread][idx] > iLastMilisec) - { - bHaveFullSet = true; - break; //We are out of the requested time period - } - - iEarliestStamp = ppTimestamps[iThread][idx]; - iEarliestHashCnt = ppHashCounts[iThread][idx]; - } - - if (!bHaveFullSet || iEarliestStamp == 0 || iLastestStamp == 0) - return nan(""); - - //Don't think that can happen, but just in case - if (iLastestStamp - iEarliestStamp == 0) - return nan(""); - - double fHashes, fTime; - fHashes = iLastestHashCnt - iEarliestHashCnt; - fTime = iLastestStamp - iEarliestStamp; - fTime /= 1000.0; - - return fHashes / fTime; -} - -void telemetry::push_perf_value(size_t iThd, uint64_t iHashCount, uint64_t iTimestamp) -{ - size_t iTop = iBucketTop[iThd]; - ppHashCounts[iThd][iTop] = iHashCount; - ppTimestamps[iThd][iTop] = iTimestamp; - - iBucketTop[iThd] = (iTop + 1) & iBucketMask; -} - -minethd::minethd(miner_work& pWork, size_t iNo, bool double_work, bool no_prefetch, int64_t affinity) -{ - oWork = pWork; - bQuit = 0; - iThreadNo = (uint8_t)iNo; - iJobNo = 0; - iHashCount = 0; - iTimestamp = 0; - bNoPrefetch = no_prefetch; - this->affinity = affinity; - - std::lock_guard<std::mutex> lock(work_thd_mtx); - if(double_work) - oWorkThd = std::thread(&minethd::double_work_main, this); - else - oWorkThd = std::thread(&minethd::work_main, this); -} - -std::atomic<uint64_t> minethd::iGlobalJobNo; -std::atomic<uint64_t> minethd::iConsumeCnt; //Threads get jobs as they are initialized -minethd::miner_work minethd::oGlobalWork; -uint64_t minethd::iThreadCount = 0; - -cryptonight_ctx* minethd_alloc_ctx() -{ - cryptonight_ctx* ctx; - alloc_msg msg = { 0 }; - - switch (jconf::inst()->GetSlowMemSetting()) - { - case jconf::never_use: - ctx = cryptonight_alloc_ctx(1, 1, &msg); - if (ctx == NULL) - printer::inst()->print_msg(L0, "MEMORY ALLOC FAILED: %s", msg.warning); - return ctx; - - case jconf::no_mlck: - ctx = cryptonight_alloc_ctx(1, 0, &msg); - if (ctx == NULL) - printer::inst()->print_msg(L0, "MEMORY ALLOC FAILED: %s", msg.warning); - return ctx; - - case jconf::print_warning: - ctx = cryptonight_alloc_ctx(1, 1, &msg); - if (msg.warning != NULL) - printer::inst()->print_msg(L0, "MEMORY ALLOC FAILED: %s", msg.warning); - if (ctx == NULL) - ctx = cryptonight_alloc_ctx(0, 0, NULL); - return ctx; - - case jconf::always_use: - return cryptonight_alloc_ctx(0, 0, NULL); - - case jconf::unknown_value: - return NULL; //Shut up compiler - } - - return nullptr; //Should never happen -} - -bool minethd::self_test() -{ - alloc_msg msg = { 0 }; - size_t res; - bool fatal = false; - - switch (jconf::inst()->GetSlowMemSetting()) - { - case jconf::never_use: - res = cryptonight_init(1, 1, &msg); - fatal = true; - break; - - case jconf::no_mlck: - res = cryptonight_init(1, 0, &msg); - fatal = true; - break; - - case jconf::print_warning: - res = cryptonight_init(1, 1, &msg); - break; - - case jconf::always_use: - res = cryptonight_init(0, 0, &msg); - break; - - case jconf::unknown_value: - default: - return false; //Shut up compiler - } - - if(msg.warning != nullptr) - printer::inst()->print_msg(L0, "MEMORY INIT ERROR: %s", msg.warning); - - if(res == 0 && fatal) - return false; - - cryptonight_ctx *ctx0, *ctx1; - if((ctx0 = minethd_alloc_ctx()) == nullptr) - return false; - - if((ctx1 = minethd_alloc_ctx()) == nullptr) - { - cryptonight_free_ctx(ctx0); - return false; - } - - unsigned char out[64]; - bool bResult; - - cn_hash_fun hashf; - cn_hash_fun_dbl hashdf; - - hashf = func_selector(jconf::inst()->HaveHardwareAes(), false); - hashf("This is a test", 14, out, ctx0); - bResult = memcmp(out, "\xa0\x84\xf0\x1d\x14\x37\xa0\x9c\x69\x85\x40\x1b\x60\xd4\x35\x54\xae\x10\x58\x02\xc5\xf5\xd8\xa9\xb3\x25\x36\x49\xc0\xbe\x66\x05", 32) == 0; - - hashf = func_selector(jconf::inst()->HaveHardwareAes(), true); - hashf("This is a test", 14, out, ctx0); - bResult &= memcmp(out, "\xa0\x84\xf0\x1d\x14\x37\xa0\x9c\x69\x85\x40\x1b\x60\xd4\x35\x54\xae\x10\x58\x02\xc5\xf5\xd8\xa9\xb3\x25\x36\x49\xc0\xbe\x66\x05", 32) == 0; - - hashdf = func_dbl_selector(jconf::inst()->HaveHardwareAes(), false); - hashdf("The quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy log", 43, out, ctx0, ctx1); - bResult &= memcmp(out, "\x3e\xbb\x7f\x9f\x7d\x27\x3d\x7c\x31\x8d\x86\x94\x77\x55\x0c\xc8\x00\xcf\xb1\x1b\x0c\xad\xb7\xff\xbd\xf6\xf8\x9f\x3a\x47\x1c\x59" - "\xb4\x77\xd5\x02\xe4\xd8\x48\x7f\x42\xdf\xe3\x8e\xed\x73\x81\x7a\xda\x91\xb7\xe2\x63\xd2\x91\x71\xb6\x5c\x44\x3a\x01\x2a\x41\x22", 64) == 0; - - hashdf = func_dbl_selector(jconf::inst()->HaveHardwareAes(), true); - hashdf("The quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy log", 43, out, ctx0, ctx1); - bResult &= memcmp(out, "\x3e\xbb\x7f\x9f\x7d\x27\x3d\x7c\x31\x8d\x86\x94\x77\x55\x0c\xc8\x00\xcf\xb1\x1b\x0c\xad\xb7\xff\xbd\xf6\xf8\x9f\x3a\x47\x1c\x59" - "\xb4\x77\xd5\x02\xe4\xd8\x48\x7f\x42\xdf\xe3\x8e\xed\x73\x81\x7a\xda\x91\xb7\xe2\x63\xd2\x91\x71\xb6\x5c\x44\x3a\x01\x2a\x41\x22", 64) == 0; - - cryptonight_free_ctx(ctx0); - cryptonight_free_ctx(ctx1); - - if(!bResult) - printer::inst()->print_msg(L0, - "Cryptonight hash self-test failed. This might be caused by bad compiler optimizations."); - - return bResult; -} - -std::vector<minethd*>* minethd::thread_starter(miner_work& pWork) -{ - iGlobalJobNo = 0; - iConsumeCnt = 0; - std::vector<minethd*>* pvThreads = new std::vector<minethd*>; - - //Launch the requested number of single and double threads, to distribute - //load evenly we need to alternate single and double threads - size_t i, n = jconf::inst()->GetThreadCount(); - pvThreads->reserve(n); - - jconf::thd_cfg cfg; - for (i = 0; i < n; i++) - { - jconf::inst()->GetThreadConfig(i, cfg); - - minethd* thd = new minethd(pWork, i, cfg.bDoubleMode, cfg.bNoPrefetch, cfg.iCpuAff); - pvThreads->push_back(thd); - - if(cfg.iCpuAff >= 0) - printer::inst()->print_msg(L1, "Starting %s thread, affinity: %d.", cfg.bDoubleMode ? "double" : "single", (int)cfg.iCpuAff); - else - printer::inst()->print_msg(L1, "Starting %s thread, no affinity.", cfg.bDoubleMode ? "double" : "single"); - } - - iThreadCount = n; - return pvThreads; -} - -void minethd::switch_work(miner_work& pWork) -{ - // iConsumeCnt is a basic lock-like polling mechanism just in case we happen to push work - // faster than threads can consume them. This should never happen in real life. - // Pool cant physically send jobs faster than every 250ms or so due to net latency. - - while (iConsumeCnt.load(std::memory_order_seq_cst) < iThreadCount) - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - oGlobalWork = pWork; - iConsumeCnt.store(0, std::memory_order_seq_cst); - iGlobalJobNo++; -} - -void minethd::consume_work() -{ - memcpy(&oWork, &oGlobalWork, sizeof(miner_work)); - iJobNo++; - iConsumeCnt++; -} - -minethd::cn_hash_fun minethd::func_selector(bool bHaveAes, bool bNoPrefetch) -{ - // We have two independent flag bits in the functions - // therefore we will build a binary digit and select the - // function as a two digit binary - // Digit order SOFT_AES, NO_PREFETCH - - static const cn_hash_fun func_table[4] = { - cryptonight_hash<0x80000, MEMORY, false, false>, - cryptonight_hash<0x80000, MEMORY, false, true>, - cryptonight_hash<0x80000, MEMORY, true, false>, - cryptonight_hash<0x80000, MEMORY, true, true> - }; - - std::bitset<2> digit; - digit.set(0, !bNoPrefetch); - digit.set(1, !bHaveAes); - - return func_table[digit.to_ulong()]; -} - -void minethd::pin_thd_affinity() -{ - //Lock is needed because we need to use oWorkThd - std::lock_guard<std::mutex> lock(work_thd_mtx); - - // pin memory to NUMA node - bindMemoryToNUMANode(affinity); - -#if defined(__APPLE__) - printer::inst()->print_msg(L1, "WARNING on MacOS thread affinity is only advisory."); -#endif - thd_setaffinity(oWorkThd.native_handle(), affinity); -} - -void minethd::work_main() -{ - if(affinity >= 0) //-1 means no affinity - pin_thd_affinity(); - - cn_hash_fun hash_fun; - cryptonight_ctx* ctx; - uint64_t iCount = 0; - uint64_t* piHashVal; - uint32_t* piNonce; - job_result result; - - hash_fun = func_selector(jconf::inst()->HaveHardwareAes(), bNoPrefetch); - ctx = minethd_alloc_ctx(); - - piHashVal = (uint64_t*)(result.bResult + 24); - piNonce = (uint32_t*)(oWork.bWorkBlob + 39); - iConsumeCnt++; - - while (bQuit == 0) - { - if (oWork.bStall) - { - /* We are stalled here because the executor didn't find a job for us yet, - either because of network latency, or a socket problem. Since we are - raison d'etre of this software it us sensible to just wait until we have something*/ - - while (iGlobalJobNo.load(std::memory_order_relaxed) == iJobNo) - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - consume_work(); - continue; - } - - if(oWork.bNiceHash) - result.iNonce = calc_nicehash_nonce(*piNonce, oWork.iResumeCnt); - else - result.iNonce = calc_start_nonce(oWork.iResumeCnt); - - assert(sizeof(job_result::sJobID) == sizeof(pool_job::sJobID)); - memcpy(result.sJobID, oWork.sJobID, sizeof(job_result::sJobID)); - - while(iGlobalJobNo.load(std::memory_order_relaxed) == iJobNo) - { - if ((iCount & 0xF) == 0) //Store stats every 16 hashes - { - using namespace std::chrono; - uint64_t iStamp = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count(); - iHashCount.store(iCount, std::memory_order_relaxed); - iTimestamp.store(iStamp, std::memory_order_relaxed); - } - iCount++; - - *piNonce = ++result.iNonce; - - hash_fun(oWork.bWorkBlob, oWork.iWorkSize, result.bResult, ctx); - - if (*piHashVal < oWork.iTarget) - executor::inst()->push_event(ex_event(result, oWork.iPoolId)); - - std::this_thread::yield(); - } - - consume_work(); - } - - cryptonight_free_ctx(ctx); -} - -minethd::cn_hash_fun_dbl minethd::func_dbl_selector(bool bHaveAes, bool bNoPrefetch) -{ - // We have two independent flag bits in the functions - // therefore we will build a binary digit and select the - // function as a two digit binary - // Digit order SOFT_AES, NO_PREFETCH - - static const cn_hash_fun_dbl func_table[4] = { - cryptonight_double_hash<0x80000, MEMORY, false, false>, - cryptonight_double_hash<0x80000, MEMORY, false, true>, - cryptonight_double_hash<0x80000, MEMORY, true, false>, - cryptonight_double_hash<0x80000, MEMORY, true, true> - }; - - std::bitset<2> digit; - digit.set(0, !bNoPrefetch); - digit.set(1, !bHaveAes); - - return func_table[digit.to_ulong()]; -} - -void minethd::double_work_main() -{ - if(affinity >= 0) //-1 means no affinity - pin_thd_affinity(); - - cn_hash_fun_dbl hash_fun; - cryptonight_ctx* ctx0; - cryptonight_ctx* ctx1; - uint64_t iCount = 0; - uint64_t *piHashVal0, *piHashVal1; - uint32_t *piNonce0, *piNonce1; - uint8_t bDoubleHashOut[64]; - uint8_t bDoubleWorkBlob[sizeof(miner_work::bWorkBlob) * 2]; - uint32_t iNonce; - job_result res; - - hash_fun = func_dbl_selector(jconf::inst()->HaveHardwareAes(), bNoPrefetch); - ctx0 = minethd_alloc_ctx(); - ctx1 = minethd_alloc_ctx(); - - piHashVal0 = (uint64_t*)(bDoubleHashOut + 24); - piHashVal1 = (uint64_t*)(bDoubleHashOut + 32 + 24); - piNonce0 = (uint32_t*)(bDoubleWorkBlob + 39); - piNonce1 = nullptr; - - iConsumeCnt++; - - while (bQuit == 0) - { - if (oWork.bStall) - { - /* We are stalled here because the executor didn't find a job for us yet, - either because of network latency, or a socket problem. Since we are - raison d'etre of this software it us sensible to just wait until we have something*/ - - while (iGlobalJobNo.load(std::memory_order_relaxed) == iJobNo) - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - consume_work(); - memcpy(bDoubleWorkBlob, oWork.bWorkBlob, oWork.iWorkSize); - memcpy(bDoubleWorkBlob + oWork.iWorkSize, oWork.bWorkBlob, oWork.iWorkSize); - piNonce1 = (uint32_t*)(bDoubleWorkBlob + oWork.iWorkSize + 39); - continue; - } - - if(oWork.bNiceHash) - iNonce = calc_nicehash_nonce(*piNonce0, oWork.iResumeCnt); - else - iNonce = calc_start_nonce(oWork.iResumeCnt); - - assert(sizeof(job_result::sJobID) == sizeof(pool_job::sJobID)); - - while (iGlobalJobNo.load(std::memory_order_relaxed) == iJobNo) - { - if ((iCount & 0x7) == 0) //Store stats every 16 hashes - { - using namespace std::chrono; - uint64_t iStamp = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count(); - iHashCount.store(iCount, std::memory_order_relaxed); - iTimestamp.store(iStamp, std::memory_order_relaxed); - } - - iCount += 2; - - *piNonce0 = ++iNonce; - *piNonce1 = ++iNonce; - - hash_fun(bDoubleWorkBlob, oWork.iWorkSize, bDoubleHashOut, ctx0, ctx1); - - if (*piHashVal0 < oWork.iTarget) - executor::inst()->push_event(ex_event(job_result(oWork.sJobID, iNonce-1, bDoubleHashOut), oWork.iPoolId)); - - if (*piHashVal1 < oWork.iTarget) - executor::inst()->push_event(ex_event(job_result(oWork.sJobID, iNonce, bDoubleHashOut + 32), oWork.iPoolId)); - - std::this_thread::yield(); - } - - consume_work(); - memcpy(bDoubleWorkBlob, oWork.bWorkBlob, oWork.iWorkSize); - memcpy(bDoubleWorkBlob + oWork.iWorkSize, oWork.bWorkBlob, oWork.iWorkSize); - piNonce1 = (uint32_t*)(bDoubleWorkBlob + oWork.iWorkSize + 39); - } - - cryptonight_free_ctx(ctx0); - cryptonight_free_ctx(ctx1); -} |