diff options
author | psychocrypt <psychocrypt@users.noreply.github.com> | 2017-09-29 20:32:31 +0200 |
---|---|---|
committer | psychocrypt <psychocrypt@users.noreply.github.com> | 2017-09-30 23:46:08 +0200 |
commit | cc429b68fadc502b981fd0acd64a5ff6e2ae1d15 (patch) | |
tree | 3fb23fc4db15dbdd08af4c7ea20134b9d82e58fd /xmrstak/backend/cpu/minethd.cpp | |
parent | e5b0319d5a9f58762fa934ad700113908940cb31 (diff) | |
download | xmr-stak-cc429b68fadc502b981fd0acd64a5ff6e2ae1d15.zip xmr-stak-cc429b68fadc502b981fd0acd64a5ff6e2ae1d15.tar.gz |
group files
- move source code to `src`
- categorize files and move to group folder
- change upper case class files to lower case
- change C++ header to `*.hpp`
Diffstat (limited to 'xmrstak/backend/cpu/minethd.cpp')
-rw-r--r-- | xmrstak/backend/cpu/minethd.cpp | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/xmrstak/backend/cpu/minethd.cpp b/xmrstak/backend/cpu/minethd.cpp new file mode 100644 index 0000000..3ffdf99 --- /dev/null +++ b/xmrstak/backend/cpu/minethd.cpp @@ -0,0 +1,508 @@ +/* + * 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" +#include "../IBackend.hpp" +#include "../GlobalStates.hpp" +#include "../../ConfigEditor.hpp" +#include "../../Params.hpp" +#include "../../jconf.h" + +#include "../../executor.h" +#include "minethd.h" +#include "./jconf.h" +#include "../../crypto/cryptonight_aesni.h" +#include "../../hwlocMemory.hpp" +#include "../miner_work.h" + +#ifndef CONF_NO_HWLOC +# include "autoAdjustHwloc.hpp" +#else +# include "autoAdjust.hpp" +#endif + + +#ifdef _WIN32 +#include <windows.h> + +namespace xmrstak +{ +namespace cpu +{ +void minethd::thd_setaffinity(std::thread::native_handle_type h, uint64_t cpu_id) +{ + SetThreadAffinityMask(h, 1ULL << cpu_id); +} + +} // namespace cpu +} // namespace xmrstak + +#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 + +namespace xmrstak +{ +namespace cpu +{ + +void minethd::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 +} + +} // namespace cpu +} // namespace xmrstak + +#endif // _WIN32 + + +namespace xmrstak +{ +namespace cpu +{ + +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; + 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); +} + +cryptonight_ctx* minethd::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<IBackend*> minethd::thread_starter(uint32_t threadOffset, miner_work& pWork) +{ + std::vector<IBackend*> pvThreads; + + if(!ConfigEditor::file_exist(Params::inst().configFileCPU)) + { + autoAdjust adjust; + if(!adjust.printConfig()) + return pvThreads; + } + + if(!jconf::inst()->parse_config()) + { + win_exit(); + } + + + //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); + + // \todo need thread offset + minethd* thd = new minethd(pWork, i + threadOffset, 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"); + } + + return pvThreads; +} + +void minethd::consume_work() +{ + memcpy(&oWork, &GlobalStates::inst().inst().oGlobalWork, sizeof(miner_work)); + iJobNo++; + GlobalStates::inst().inst().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); + GlobalStates::inst().inst().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 (GlobalStates::inst().inst().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(GlobalStates::inst().inst().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; + + GlobalStates::inst().inst().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 (GlobalStates::inst().inst().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 (GlobalStates::inst().inst().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); +} + +} // namespace cpu +} // namepsace xmrstak |