summaryrefslogtreecommitdiffstats
path: root/xmrstak/backend/cpu/minethd.cpp
diff options
context:
space:
mode:
authorpsychocrypt <psychocrypt@users.noreply.github.com>2017-09-29 20:32:31 +0200
committerpsychocrypt <psychocrypt@users.noreply.github.com>2017-09-30 23:46:08 +0200
commitcc429b68fadc502b981fd0acd64a5ff6e2ae1d15 (patch)
tree3fb23fc4db15dbdd08af4c7ea20134b9d82e58fd /xmrstak/backend/cpu/minethd.cpp
parente5b0319d5a9f58762fa934ad700113908940cb31 (diff)
downloadxmr-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.cpp508
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
OpenPOWER on IntegriCloud