mirror of
https://github.com/jwinterm/xmr-stak.git
synced 2026-10-20 06:08:37 +02:00
update rot files
- add config file editor - move telemetry out of minethd
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <streambuf>
|
||||
#include <regex>
|
||||
|
||||
|
||||
namespace xmrstak
|
||||
{
|
||||
|
||||
struct ConfigEditor
|
||||
{
|
||||
std::string m_fileContent;
|
||||
|
||||
ConfigEditor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static bool file_exist( const std::string filename)
|
||||
{
|
||||
std::ifstream fstream(filename);
|
||||
return fstream.good();
|
||||
}
|
||||
|
||||
void set( const std::string && content)
|
||||
{
|
||||
m_fileContent = content;
|
||||
}
|
||||
|
||||
bool load(const std::string filename)
|
||||
{
|
||||
std::ifstream fstream(filename);
|
||||
m_fileContent = std::string(
|
||||
(std::istreambuf_iterator<char>(fstream)),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
return fstream.good();
|
||||
}
|
||||
|
||||
void write(const std::string filename)
|
||||
{
|
||||
std::ofstream out(filename);
|
||||
out << m_fileContent;
|
||||
out.close();
|
||||
}
|
||||
|
||||
void replace(const std::string search, const std::string substring)
|
||||
{
|
||||
m_fileContent = std::regex_replace(m_fileContent, std::regex(search), substring);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namepsace xmrstak
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
#pragma once
|
||||
#include "jconf.h"
|
||||
#include "console.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif // _WIN32
|
||||
|
||||
// Mask bits between h and l and return the value
|
||||
// This enables us to put in values exactly like in the manual
|
||||
// For example EBX[31:22] is get_masked(cpu_info[1], 31, 22)
|
||||
inline int32_t get_masked(int32_t val, int32_t h, int32_t l)
|
||||
{
|
||||
val &= (0x7FFFFFFF >> (31-(h-l))) << l;
|
||||
return val >> l;
|
||||
}
|
||||
|
||||
class autoAdjust
|
||||
{
|
||||
public:
|
||||
|
||||
autoAdjust()
|
||||
{
|
||||
}
|
||||
|
||||
void printConfig()
|
||||
{
|
||||
printer::inst()->print_str("The configuration for 'cpu_threads_conf' in your config file is 'null'.\n");
|
||||
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");
|
||||
|
||||
if(!detectL3Size() || L3KB_size < 1024 || L3KB_size > 102400)
|
||||
{
|
||||
if(L3KB_size < 1024 || L3KB_size > 102400)
|
||||
printer::inst()->print_msg(L0, "Autoconf failed: L3 size sanity check failed - %u KB.", L3KB_size);
|
||||
|
||||
printer::inst()->print_msg(L0, "Autoconf failed: Printing config for a single thread. Please try to add new ones until the hashrate slows down.");
|
||||
printer::inst()->print_str("\n**************** 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**************** Copy&Paste END ****************\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printer::inst()->print_msg(L0, "Autoconf L3 size detected at %u KB.", L3KB_size);
|
||||
|
||||
detectCPUConf();
|
||||
|
||||
printer::inst()->print_msg(L0, "Autoconf core count detected as %u on %s.", corecnt,
|
||||
linux_layout ? "Linux" : "Windows");
|
||||
|
||||
printer::inst()->print_str("\n**************** Copy&Paste BEGIN ****************\n\n");
|
||||
printer::inst()->print_str("\"cpu_threads_conf\" :\n[\n");
|
||||
|
||||
uint32_t aff_id = 0;
|
||||
char strbuf[256];
|
||||
for(uint32_t i=0; i < corecnt; i++)
|
||||
{
|
||||
bool double_mode;
|
||||
|
||||
if(L3KB_size <= 0)
|
||||
break;
|
||||
|
||||
double_mode = L3KB_size / 2048 > (int32_t)(corecnt-i);
|
||||
|
||||
snprintf(strbuf, sizeof(strbuf), " { \"low_power_mode\" : %s, \"no_prefetch\" : true, \"affine_to_cpu\" : %u },\n",
|
||||
double_mode ? "true" : "false", aff_id);
|
||||
printer::inst()->print_str(strbuf);
|
||||
|
||||
if(!linux_layout || old_amd)
|
||||
{
|
||||
aff_id += 2;
|
||||
|
||||
if(aff_id >= corecnt)
|
||||
aff_id = 1;
|
||||
}
|
||||
else
|
||||
aff_id++;
|
||||
|
||||
if(double_mode)
|
||||
L3KB_size -= 4096;
|
||||
else
|
||||
L3KB_size -= 2048;
|
||||
}
|
||||
|
||||
printer::inst()->print_str("],\n\n**************** Copy&Paste END ****************\n");
|
||||
}
|
||||
|
||||
private:
|
||||
bool detectL3Size()
|
||||
{
|
||||
int32_t cpu_info[4];
|
||||
char cpustr[13] = {0};
|
||||
|
||||
jconf::cpuid(0, 0, cpu_info);
|
||||
memcpy(cpustr, &cpu_info[1], 4);
|
||||
memcpy(cpustr+4, &cpu_info[3], 4);
|
||||
memcpy(cpustr+8, &cpu_info[2], 4);
|
||||
|
||||
if(strcmp(cpustr, "GenuineIntel") == 0)
|
||||
{
|
||||
jconf::cpuid(4, 3, cpu_info);
|
||||
|
||||
if(get_masked(cpu_info[0], 7, 5) != 3)
|
||||
{
|
||||
printer::inst()->print_msg(L0, "Autoconf failed: Couln't find L3 cache page.");
|
||||
return false;
|
||||
}
|
||||
|
||||
L3KB_size = ((get_masked(cpu_info[1], 31, 22) + 1) * (get_masked(cpu_info[1], 21, 12) + 1) *
|
||||
(get_masked(cpu_info[1], 11, 0) + 1) * (cpu_info[2] + 1)) / 1024;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(strcmp(cpustr, "AuthenticAMD") == 0)
|
||||
{
|
||||
jconf::cpuid(0x80000006, 0, cpu_info);
|
||||
|
||||
L3KB_size = get_masked(cpu_info[3], 31, 18) * 512;
|
||||
|
||||
jconf::cpuid(1, 0, cpu_info);
|
||||
if(get_masked(cpu_info[0], 11, 8) < 0x17) //0x17h is Zen
|
||||
old_amd = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
printer::inst()->print_msg(L0, "Autoconf failed: Unknown CPU type: %s.", cpustr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void detectCPUConf()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
corecnt = info.dwNumberOfProcessors;
|
||||
linux_layout = false;
|
||||
#else
|
||||
corecnt = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
linux_layout = true;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
int32_t L3KB_size = 0;
|
||||
uint32_t corecnt;
|
||||
bool old_amd = false;
|
||||
bool linux_layout;
|
||||
};
|
||||
@@ -1,195 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "console.h"
|
||||
#include <hwloc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif // _WIN32
|
||||
|
||||
class autoAdjust
|
||||
{
|
||||
public:
|
||||
|
||||
autoAdjust()
|
||||
{
|
||||
}
|
||||
|
||||
void printConfig()
|
||||
{
|
||||
printer::inst()->print_str("The configuration for 'cpu_threads_conf' in your config file is 'null'.\n");
|
||||
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");
|
||||
|
||||
hwloc_topology_t topology;
|
||||
hwloc_topology_init(&topology);
|
||||
hwloc_topology_load(topology);
|
||||
|
||||
try
|
||||
{
|
||||
std::vector<hwloc_obj_t> tlcs;
|
||||
tlcs.reserve(16);
|
||||
results.reserve(16);
|
||||
|
||||
findChildrenCaches(hwloc_get_root_obj(topology),
|
||||
[&tlcs](hwloc_obj_t found) { tlcs.emplace_back(found); } );
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t hashSize = 2 * 1024 * 1024;
|
||||
std::vector<uint32_t> results;
|
||||
|
||||
template<typename func>
|
||||
inline void findChildrenByType(hwloc_obj_t obj, hwloc_obj_type_t type, func lambda)
|
||||
{
|
||||
for(size_t i=0; i < obj->arity; i++)
|
||||
{
|
||||
if(obj->children[i]->type == type)
|
||||
lambda(obj->children[i]);
|
||||
else
|
||||
findChildrenByType(obj->children[i], type, lambda);
|
||||
}
|
||||
}
|
||||
|
||||
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<typename func>
|
||||
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");
|
||||
return value == nullptr || value[0] != '1';
|
||||
}
|
||||
|
||||
// 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(!isCacheObject(obj->children[0]))
|
||||
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(isCacheExclusive(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(isCacheObject(l2obj) && l2obj->attr != nullptr && l2obj->attr->cache.size >= hashSize)
|
||||
cacheSize += hashSize;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<hwloc_obj_t> cores;
|
||||
cores.reserve(16);
|
||||
findChildrenByType(obj, HWLOC_OBJ_CORE, [&cores](hwloc_obj_t found) { cores.emplace_back(found); } );
|
||||
|
||||
size_t cacheHashes = (cacheSize + hashSize/2) / 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++;
|
||||
}
|
||||
}
|
||||
};
|
||||
+18
-32
@@ -22,15 +22,13 @@
|
||||
*/
|
||||
|
||||
#include "executor.h"
|
||||
#include "minethd.h"
|
||||
#include "backend/miner_work.h"
|
||||
#include "backend/GlobalStates.hpp"
|
||||
#include "backend/BackendConnector.hpp"
|
||||
#include "jconf.h"
|
||||
#include "console.h"
|
||||
#include "donate-level.h"
|
||||
#ifndef CONF_NO_HWLOC
|
||||
# include "autoAdjustHwloc.hpp"
|
||||
#else
|
||||
# include "autoAdjust.hpp"
|
||||
#endif
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#ifndef CONF_NO_HTTPD
|
||||
@@ -48,19 +46,9 @@
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
//Do a press any key for the windows folk. *insert any key joke here*
|
||||
|
||||
#ifdef _WIN32
|
||||
void win_exit()
|
||||
{
|
||||
printer::inst()->print_str("Press any key to exit.");
|
||||
get_key();
|
||||
return;
|
||||
}
|
||||
|
||||
#define strcasecmp _stricmp
|
||||
|
||||
#else
|
||||
void win_exit() { return; }
|
||||
# define strcasecmp _stricmp
|
||||
#endif // _WIN32
|
||||
|
||||
void do_benchmark();
|
||||
@@ -109,15 +97,7 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(jconf::inst()->NeedsAutoconf())
|
||||
{
|
||||
autoAdjust adjust;
|
||||
adjust.printConfig();
|
||||
win_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!minethd::self_test())
|
||||
if (!xmrstak::BackendConnector::self_test())
|
||||
{
|
||||
win_exit();
|
||||
return 0;
|
||||
@@ -144,6 +124,12 @@ int main(int argc, char *argv[])
|
||||
printer::inst()->print_str("-------------------------------------------------------------------\n");
|
||||
printer::inst()->print_str( XMR_STAK_NAME" " XMR_STAK_VERSION " mining software, CPU Version.\n");
|
||||
printer::inst()->print_str("Based on CPU mining code by wolf9466 (heavily optimized by fireice_uk).\n");
|
||||
#ifndef CONF_NO_CUDA
|
||||
printer::inst()->print_str("NVIDIA mining code was written by KlausT and psychocrypt.\n");
|
||||
#endif
|
||||
#ifndef CONF_NO_OPENCL
|
||||
printer::inst()->print_str("AMD mining code was written by wolf9466.\n");
|
||||
#endif
|
||||
printer::inst()->print_str("Brought to you by fireice_uk and psychocrypt under GPLv3.\n\n");
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "Configurable dev donation level is set to %.1f %%\n\n", fDevDonationLevel * 100.0);
|
||||
@@ -196,20 +182,20 @@ int main(int argc, char *argv[])
|
||||
void do_benchmark()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
std::vector<minethd*>* pvThreads;
|
||||
std::vector<xmrstak::IBackend*>* pvThreads;
|
||||
|
||||
printer::inst()->print_msg(L0, "Running a 60 second benchmark...");
|
||||
|
||||
uint8_t work[76] = {0};
|
||||
minethd::miner_work oWork = minethd::miner_work("", work, sizeof(work), 0, 0, false, 0);
|
||||
pvThreads = minethd::thread_starter(oWork);
|
||||
xmrstak::miner_work oWork = xmrstak::miner_work("", work, sizeof(work), 0, 0, 0);
|
||||
pvThreads = xmrstak::BackendConnector::thread_starter(oWork);
|
||||
|
||||
uint64_t iStartStamp = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(60));
|
||||
|
||||
oWork = minethd::miner_work();
|
||||
minethd::switch_work(oWork);
|
||||
oWork = xmrstak::miner_work();
|
||||
xmrstak::GlobalStates::switch_work(oWork);
|
||||
|
||||
double fTotalHps = 0.0;
|
||||
for (uint32_t i = 0; i < pvThreads->size(); i++)
|
||||
|
||||
+96
-184
@@ -1,184 +1,96 @@
|
||||
/*
|
||||
* Thread configuration for each thread. Make sure it matches the number above.
|
||||
* low_power_mode - This mode will double the cache usage, and double the single thread performance. It will
|
||||
* consume much less power (as less cores are working), but will max out at around 80-85% of
|
||||
* the maximum performance.
|
||||
*
|
||||
* no_prefetch - Some sytems can gain up to extra 5% here, but sometimes it will have no difference or make
|
||||
* things slower.
|
||||
*
|
||||
* affine_to_cpu - This can be either false (no affinity), or the CPU core number. Note that on hyperthreading
|
||||
* systems it is better to assign threads to physical cores. On Windows this usually means selecting
|
||||
* even or odd numbered cpu numbers. For Linux it will be usually the lower CPU numbers, so for a 4
|
||||
* physical core CPU you should select cpu numbers 0-3.
|
||||
*
|
||||
* On the first run the miner will look at your system and suggest a basic configuration that will work,
|
||||
* you can try to tweak it from there to get the best performance.
|
||||
*
|
||||
* A filled out configuration should look like this:
|
||||
* "cpu_threads_conf" :
|
||||
* [
|
||||
* { "low_power_mode" : false, "no_prefetch" : true, "affine_to_cpu" : 0 },
|
||||
* { "low_power_mode" : false, "no_prefetch" : true, "affine_to_cpu" : 1 },
|
||||
* ],
|
||||
*/
|
||||
"cpu_threads_conf" :
|
||||
null,
|
||||
|
||||
/*
|
||||
* LARGE PAGE SUPPORT
|
||||
* Large pages need a properly set up OS. It can be difficult if you are not used to systems administration,
|
||||
* but the performance results are worth the trouble - you will get around 20% boost. Slow memory mode is
|
||||
* meant as a backup, you won't get stellar results there. If you are running into trouble, especially
|
||||
* on Windows, please read the common issues in the README.
|
||||
*
|
||||
* By default we will try to allocate large pages. This means you need to "Run As Administrator" on Windows.
|
||||
* You need to edit your system's group policies to enable locking large pages. Here are the steps from MSDN
|
||||
*
|
||||
* 1. On the Start menu, click Run. In the Open box, type gpedit.msc.
|
||||
* 2. On the Local Group Policy Editor console, expand Computer Configuration, and then expand Windows Settings.
|
||||
* 3. Expand Security Settings, and then expand Local Policies.
|
||||
* 4. Select the User Rights Assignment folder.
|
||||
* 5. The policies will be displayed in the details pane.
|
||||
* 6. In the pane, double-click Lock pages in memory.
|
||||
* 7. In the Local Security Setting – Lock pages in memory dialog box, click Add User or Group.
|
||||
* 8. In the Select Users, Service Accounts, or Groups dialog box, add an account that you will run the miner on
|
||||
* 9. Reboot for change to take effect.
|
||||
*
|
||||
* Windows also tends to fragment memory a lot. If you are running on a system with 4-8GB of RAM you might need
|
||||
* to switch off all the auto-start applications and reboot to have a large enough chunk of contiguous memory.
|
||||
*
|
||||
* On Linux you will need to configure large page support "sudo sysctl -w vm.nr_hugepages=128" and increase your
|
||||
* ulimit -l. To do do this you need to add following lines to /etc/security/limits.conf - "* soft memlock 262144"
|
||||
* and "* hard memlock 262144". You can also do it Windows-style and simply run-as-root, but this is NOT
|
||||
* recommended for security reasons.
|
||||
*
|
||||
* Memory locking means that the kernel can't swap out the page to disk - something that is unlikely to happen on a
|
||||
* command line system that isn't starved of memory. I haven't observed any difference on a CLI Linux system between
|
||||
* locked and unlocked memory. If that is your setup see option "no_mlck".
|
||||
*/
|
||||
|
||||
/*
|
||||
* use_slow_memory defines our behavior with regards to large pages. There are three possible options here:
|
||||
* always - Don't even try to use large pages. Always use slow memory.
|
||||
* warn - We will try to use large pages, but fall back to slow memory if that fails.
|
||||
* no_mlck - This option is only relevant on Linux, where we can use large pages without locking memory.
|
||||
* It will never use slow memory, but it won't attempt to mlock
|
||||
* never - If we fail to allocate large pages we will print an error and exit.
|
||||
*/
|
||||
"use_slow_memory" : "warn",
|
||||
|
||||
/*
|
||||
* NiceHash mode
|
||||
* nicehash_nonce - Limit the nonce to 3 bytes as required by nicehash. This cuts all the safety margins, and
|
||||
* if a block isn't found within 30 minutes then you might run into nonce collisions. Number
|
||||
* of threads in this mode is hard-limited to 32.
|
||||
*/
|
||||
"nicehash_nonce" : false,
|
||||
|
||||
/*
|
||||
* Manual hardware AES override
|
||||
*
|
||||
* Some VMs don't report AES capability correctly. You can set this value to true to enforce hardware AES or
|
||||
* to false to force disable AES or null to let the miner decide if AES is used.
|
||||
*
|
||||
* WARNING: setting this to true on a CPU that doesn't support hardware AES will crash the miner.
|
||||
*/
|
||||
"aes_override" : null,
|
||||
|
||||
/*
|
||||
* TLS Settings
|
||||
* If you need real security, make sure tls_secure_algo is enabled (otherwise MITM attack can downgrade encryption
|
||||
* to trivially breakable stuff like DES and MD5), and verify the server's fingerprint through a trusted channel.
|
||||
*
|
||||
* use_tls - This option will make us connect using Transport Layer Security.
|
||||
* tls_secure_algo - Use only secure algorithms. This will make us quit with an error if we can't negotiate a secure algo.
|
||||
* tls_fingerprint - Server's SHA256 fingerprint. If this string is non-empty then we will check the server's cert against it.
|
||||
*/
|
||||
"use_tls" : false,
|
||||
"tls_secure_algo" : true,
|
||||
"tls_fingerprint" : "",
|
||||
|
||||
/*
|
||||
* pool_address - Pool address should be in the form "pool.supportxmr.com:3333". Only stratum pools are supported.
|
||||
* wallet_address - Your wallet, or pool login.
|
||||
* pool_password - Can be empty in most cases or "x".
|
||||
*
|
||||
* We feature pools up to 1MH/s. For a more complete list see M5M400's pool list at www.moneropools.com
|
||||
*/
|
||||
"pool_address" : "pool.usxmrpool.com:3333",
|
||||
"wallet_address" : "",
|
||||
"pool_password" : "",
|
||||
|
||||
/*
|
||||
* Network timeouts.
|
||||
* Because of the way this client is written it doesn't need to constantly talk (keep-alive) to the server to make
|
||||
* sure it is there. We detect a buggy / overloaded server by the call timeout. The default values will be ok for
|
||||
* nearly all cases. If they aren't the pool has most likely overload issues. Low call timeout values are preferable -
|
||||
* long timeouts mean that we waste hashes on potentially stale jobs. Connection report will tell you how long the
|
||||
* server usually takes to process our calls.
|
||||
*
|
||||
* call_timeout - How long should we wait for a response from the server before we assume it is dead and drop the connection.
|
||||
* retry_time - How long should we wait before another connection attempt.
|
||||
* Both values are in seconds.
|
||||
* giveup_limit - Limit how many times we try to reconnect to the pool. Zero means no limit. Note that stak miners
|
||||
* don't mine while the connection is lost, so your computer's power usage goes down to idle.
|
||||
*/
|
||||
"call_timeout" : 10,
|
||||
"retry_time" : 10,
|
||||
"giveup_limit" : 0,
|
||||
|
||||
/*
|
||||
* Output control.
|
||||
* Since most people are used to miners printing all the time, that's what we do by default too. This is suboptimal
|
||||
* really, since you cannot see errors under pages and pages of text and performance stats. Given that we have internal
|
||||
* performance monitors, there is very little reason to spew out pages of text instead of concise reports.
|
||||
* Press 'h' (hashrate), 'r' (results) or 'c' (connection) to print reports.
|
||||
*
|
||||
* verbose_level - 0 - Don't print anything.
|
||||
* 1 - Print intro, connection event, disconnect event
|
||||
* 2 - All of level 1, and new job (block) event if the difficulty is different from the last job
|
||||
* 3 - All of level 1, and new job (block) event in all cases, result submission event.
|
||||
* 4 - All of level 3, and automatic hashrate report printing
|
||||
*/
|
||||
"verbose_level" : 3,
|
||||
|
||||
/*
|
||||
* Automatic hashrate report
|
||||
*
|
||||
* h_print_time - How often, in seconds, should we print a hashrate report if verbose_level is set to 4.
|
||||
* This option has no effect if verbose_level is not 4.
|
||||
*/
|
||||
"h_print_time" : 60,
|
||||
|
||||
/*
|
||||
* Daemon mode
|
||||
*
|
||||
* If you are running the process in the background and you don't need the keyboard reports, set this to true.
|
||||
* This should solve the hashrate problems on some emulated terminals.
|
||||
*/
|
||||
"daemon_mode" : false,
|
||||
|
||||
/*
|
||||
* Output file
|
||||
*
|
||||
* output_file - This option will log all output to a file.
|
||||
*
|
||||
*/
|
||||
"output_file" : "",
|
||||
|
||||
/*
|
||||
* Built-in web server
|
||||
* I like checking my hashrate on my phone. Don't you?
|
||||
* Keep in mind that you will need to set up port forwarding on your router if you want to access it from
|
||||
* outside of your home network. Ports lower than 1024 on Linux systems will require root.
|
||||
*
|
||||
* httpd_port - Port we should listen on. Default, 0, will switch off the server.
|
||||
*/
|
||||
"httpd_port" : 0,
|
||||
|
||||
/*
|
||||
* prefer_ipv4 - IPv6 preference. If the host is available on both IPv4 and IPv6 net, which one should be choose?
|
||||
* This setting will only be needed in 2020's. No need to worry about it now.
|
||||
*/
|
||||
"prefer_ipv4" : true,
|
||||
/*
|
||||
* TLS Settings
|
||||
* If you need real security, make sure tls_secure_algo is enabled (otherwise MITM attack can downgrade encryption
|
||||
* to trivially breakable stuff like DES and MD5), and verify the server's fingerprint through a trusted channel.
|
||||
*
|
||||
* use_tls - This option will make us connect using Transport Layer Security.
|
||||
* tls_secure_algo - Use only secure algorithms. This will make us quit with an error if we can't negotiate a secure algo.
|
||||
* tls_fingerprint - Server's SHA256 fingerprint. If this string is non-empty then we will check the server's cert against it.
|
||||
*/
|
||||
"use_tls" : false,
|
||||
"tls_secure_algo" : true,
|
||||
"tls_fingerprint" : "",
|
||||
|
||||
/*
|
||||
* pool_address - Pool address should be in the form "pool.supportxmr.com:3333". Only stratum pools are supported.
|
||||
* wallet_address - Your wallet, or pool login.
|
||||
* pool_password - Can be empty in most cases or "x".
|
||||
*
|
||||
* We feature pools up to 1MH/s. For a more complete list see M5M400's pool list at www.moneropools.com
|
||||
*/
|
||||
"pool_address" : "pool.usxmrpool.com:3333",
|
||||
"wallet_address" : "",
|
||||
"pool_password" : "",
|
||||
|
||||
/*
|
||||
* Network timeouts.
|
||||
* Because of the way this client is written it doesn't need to constantly talk (keep-alive) to the server to make
|
||||
* sure it is there. We detect a buggy / overloaded server by the call timeout. The default values will be ok for
|
||||
* nearly all cases. If they aren't the pool has most likely overload issues. Low call timeout values are preferable -
|
||||
* long timeouts mean that we waste hashes on potentially stale jobs. Connection report will tell you how long the
|
||||
* server usually takes to process our calls.
|
||||
*
|
||||
* call_timeout - How long should we wait for a response from the server before we assume it is dead and drop the connection.
|
||||
* retry_time - How long should we wait before another connection attempt.
|
||||
* Both values are in seconds.
|
||||
* giveup_limit - Limit how many times we try to reconnect to the pool. Zero means no limit. Note that stak miners
|
||||
* don't mine while the connection is lost, so your computer's power usage goes down to idle.
|
||||
*/
|
||||
"call_timeout" : 10,
|
||||
"retry_time" : 10,
|
||||
"giveup_limit" : 0,
|
||||
|
||||
/*
|
||||
* Output control.
|
||||
* Since most people are used to miners printing all the time, that's what we do by default too. This is suboptimal
|
||||
* really, since you cannot see errors under pages and pages of text and performance stats. Given that we have internal
|
||||
* performance monitors, there is very little reason to spew out pages of text instead of concise reports.
|
||||
* Press 'h' (hashrate), 'r' (results) or 'c' (connection) to print reports.
|
||||
*
|
||||
* verbose_level - 0 - Don't print anything.
|
||||
* 1 - Print intro, connection event, disconnect event
|
||||
* 2 - All of level 1, and new job (block) event if the difficulty is different from the last job
|
||||
* 3 - All of level 1, and new job (block) event in all cases, result submission event.
|
||||
* 4 - All of level 3, and automatic hashrate report printing
|
||||
*/
|
||||
"verbose_level" : 3,
|
||||
|
||||
/*
|
||||
* Automatic hashrate report
|
||||
*
|
||||
* h_print_time - How often, in seconds, should we print a hashrate report if verbose_level is set to 4.
|
||||
* This option has no effect if verbose_level is not 4.
|
||||
*/
|
||||
"h_print_time" : 60,
|
||||
|
||||
/*
|
||||
* Daemon mode
|
||||
*
|
||||
* If you are running the process in the background and you don't need the keyboard reports, set this to true.
|
||||
* This should solve the hashrate problems on some emulated terminals.
|
||||
*/
|
||||
"daemon_mode" : false,
|
||||
|
||||
/*
|
||||
* Output file
|
||||
*
|
||||
* output_file - This option will log all output to a file.
|
||||
*
|
||||
*/
|
||||
"output_file" : "",
|
||||
|
||||
/*
|
||||
* Built-in web server
|
||||
* I like checking my hashrate on my phone. Don't you?
|
||||
* Keep in mind that you will need to set up port forwarding on your router if you want to access it from
|
||||
* outside of your home network. Ports lower than 1024 on Linux systems will require root.
|
||||
*
|
||||
* httpd_port - Port we should listen on. Default, 0, will switch off the server.
|
||||
*/
|
||||
"httpd_port" : 0,
|
||||
|
||||
/*
|
||||
* prefer_ipv4 - IPv6 preference. If the host is available on both IPv4 and IPv6 net, which one should be choose?
|
||||
* This setting will only be needed in 2020's. No need to worry about it now.
|
||||
*/
|
||||
"prefer_ipv4" : true,
|
||||
|
||||
+16
@@ -26,6 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -211,3 +212,18 @@ void printer::print_str(const char* str)
|
||||
fflush(logfile);
|
||||
}
|
||||
}
|
||||
|
||||
//Do a press any key for the windows folk. *insert any key joke here*
|
||||
#ifdef _WIN32
|
||||
void win_exit()
|
||||
{
|
||||
printer::inst()->print_str("Press any key to exit.");
|
||||
get_key();
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
#else
|
||||
void win_exit() {
|
||||
std::exit(1);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
@@ -41,3 +41,5 @@ private:
|
||||
verbosity verbose_level;
|
||||
FILE* logfile;
|
||||
};
|
||||
|
||||
void win_exit();
|
||||
|
||||
+32
-15
@@ -29,7 +29,12 @@
|
||||
#include <time.h>
|
||||
#include "executor.h"
|
||||
#include "jpsock.h"
|
||||
#include "minethd.h"
|
||||
|
||||
#include "telemetry.h"
|
||||
#include "backend/miner_work.h"
|
||||
#include "backend/GlobalStates.hpp"
|
||||
#include "backend/BackendConnector.hpp"
|
||||
|
||||
#include "jconf.h"
|
||||
#include "console.h"
|
||||
#include "donate-level.h"
|
||||
@@ -116,8 +121,8 @@ void executor::sched_reconnect()
|
||||
printer::inst()->print_msg(L1, "Pool connection lost. Waiting %lld s before retry (attempt %llu).",
|
||||
rt, int_port(iReconnectAttempts));
|
||||
|
||||
auto work = minethd::miner_work();
|
||||
minethd::switch_work(work);
|
||||
auto work = xmrstak::miner_work();
|
||||
xmrstak::GlobalStates::switch_work(work);
|
||||
|
||||
push_timed_event(ex_event(EV_RECONNECT, usr_pool_id), rt);
|
||||
}
|
||||
@@ -229,12 +234,13 @@ void executor::on_pool_have_job(size_t pool_id, pool_job& oPoolJob)
|
||||
|
||||
jpsock* pool = pick_pool_by_id(pool_id);
|
||||
|
||||
minethd::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob,
|
||||
xmrstak::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob,
|
||||
oPoolJob.iWorkLen, oPoolJob.iResumeCnt, oPoolJob.iTarget,
|
||||
pool_id != dev_pool_id && jconf::inst()->NiceHashMode(),
|
||||
pool_id);
|
||||
|
||||
minethd::switch_work(oWork);
|
||||
oWork.iTarget32 = oPoolJob.iTarget32;
|
||||
|
||||
xmrstak::GlobalStates::switch_work(oWork);
|
||||
|
||||
if(pool_id == dev_pool_id)
|
||||
return;
|
||||
@@ -350,11 +356,13 @@ void executor::on_switch_pool(size_t pool_id)
|
||||
return;
|
||||
}
|
||||
|
||||
minethd::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob,
|
||||
xmrstak::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob,
|
||||
oPoolJob.iWorkLen, oPoolJob.iResumeCnt, oPoolJob.iTarget,
|
||||
jconf::inst()->NiceHashMode(), pool_id);
|
||||
pool_id);
|
||||
|
||||
minethd::switch_work(oWork);
|
||||
oWork.iTarget32 = oPoolJob.iTarget32;
|
||||
|
||||
xmrstak::GlobalStates::switch_work(oWork);
|
||||
|
||||
if(dev_pool->is_running())
|
||||
push_timed_event(ex_event(EV_DEV_POOL_EXIT), 5);
|
||||
@@ -365,9 +373,18 @@ void executor::ex_main()
|
||||
{
|
||||
assert(1000 % iTickTime == 0);
|
||||
|
||||
minethd::miner_work oWork = minethd::miner_work();
|
||||
pvThreads = minethd::thread_starter(oWork);
|
||||
telem = new telemetry(pvThreads->size());
|
||||
xmrstak::miner_work oWork = xmrstak::miner_work();
|
||||
|
||||
// \todo collect all backend threads
|
||||
pvThreads = xmrstak::BackendConnector::thread_starter(oWork);
|
||||
|
||||
if(pvThreads->size()==0)
|
||||
{
|
||||
printer::inst()->print_msg(L1, "ERROR: No miner backend enabled.");
|
||||
win_exit();
|
||||
}
|
||||
|
||||
telem = new xmrstak::telemetry(pvThreads->size());
|
||||
|
||||
current_pool_id = usr_pool_id;
|
||||
usr_pool = new jpsock(usr_pool_id, jconf::inst()->GetTlsSetting());
|
||||
@@ -499,9 +516,9 @@ void executor::hashrate_report(std::string& out)
|
||||
size_t i;
|
||||
|
||||
out.append("HASHRATE REPORT\n");
|
||||
out.append("| ID | 2.5s | 60s | 15m |");
|
||||
out.append("| ID | 10s | 60s | 15m |");
|
||||
if(nthd != 1)
|
||||
out.append(" ID | 2.5s | 60s | 15m |\n");
|
||||
out.append(" ID | 10s | 60s | 15m |\n");
|
||||
else
|
||||
out.append(1, '\n');
|
||||
|
||||
@@ -509,7 +526,7 @@ void executor::hashrate_report(std::string& out)
|
||||
{
|
||||
double fHps[3];
|
||||
|
||||
fHps[0] = telem->calc_telemetry_data(2500, i);
|
||||
fHps[0] = telem->calc_telemetry_data(10000, i);
|
||||
fHps[1] = telem->calc_telemetry_data(60000, i);
|
||||
fHps[2] = telem->calc_telemetry_data(900000, i);
|
||||
|
||||
|
||||
+13
-3
@@ -5,10 +5,20 @@
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <future>
|
||||
#include "telemetry.h"
|
||||
#include "backend/IBackend.hpp"
|
||||
|
||||
class jpsock;
|
||||
|
||||
|
||||
namespace xmrstak
|
||||
{
|
||||
namespace cpu
|
||||
{
|
||||
class minethd;
|
||||
class telemetry;
|
||||
|
||||
} // namespace cpu
|
||||
} // namepsace xmrstak
|
||||
|
||||
class executor
|
||||
{
|
||||
@@ -50,8 +60,8 @@ private:
|
||||
std::mutex timed_event_mutex;
|
||||
thdq<ex_event> oEventQ;
|
||||
|
||||
telemetry* telem;
|
||||
std::vector<minethd*>* pvThreads;
|
||||
xmrstak::telemetry* telem;
|
||||
std::vector<xmrstak::IBackend*>* pvThreads;
|
||||
|
||||
size_t current_pool_id;
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ using namespace rapidjson;
|
||||
/*
|
||||
* This enum needs to match index in oConfigValues, otherwise we will get a runtime error
|
||||
*/
|
||||
enum configEnum { aCpuThreadsConf, sUseSlowMem, bNiceHashMode, bAesOverride,
|
||||
enum configEnum {
|
||||
bTlsMode, bTlsSecureAlgo, sTlsFingerprint, sPoolAddr, sWalletAddr, sPoolPwd,
|
||||
iCallTimeout, iNetRetry, iGiveUpLimit, iVerboseLevel, iAutohashTime,
|
||||
bDaemonMode, sOutputFile, iHttpdPort, bPreferIpv4 };
|
||||
@@ -59,10 +59,6 @@ struct configVal {
|
||||
// Same order as in configEnum, as per comment above
|
||||
// kNullType means any type
|
||||
configVal oConfigValues[] = {
|
||||
{ aCpuThreadsConf, "cpu_threads_conf", kNullType },
|
||||
{ sUseSlowMem, "use_slow_memory", kStringType },
|
||||
{ bNiceHashMode, "nicehash_nonce", kTrueType },
|
||||
{ bAesOverride, "aes_override", kNullType },
|
||||
{ bTlsMode, "use_tls", kTrueType },
|
||||
{ bTlsSecureAlgo, "tls_secure_algo", kTrueType },
|
||||
{ sTlsFingerprint, "tls_fingerprint", kStringType },
|
||||
@@ -113,63 +109,6 @@ jconf::jconf()
|
||||
prv = new opaque_private();
|
||||
}
|
||||
|
||||
bool jconf::GetThreadConfig(size_t id, thd_cfg &cfg)
|
||||
{
|
||||
if(!prv->configValues[aCpuThreadsConf]->IsArray())
|
||||
return false;
|
||||
|
||||
if(id >= prv->configValues[aCpuThreadsConf]->Size())
|
||||
return false;
|
||||
|
||||
const Value& oThdConf = prv->configValues[aCpuThreadsConf]->GetArray()[id];
|
||||
|
||||
if(!oThdConf.IsObject())
|
||||
return false;
|
||||
|
||||
const Value *mode, *no_prefetch, *aff;
|
||||
mode = GetObjectMember(oThdConf, "low_power_mode");
|
||||
no_prefetch = GetObjectMember(oThdConf, "no_prefetch");
|
||||
aff = GetObjectMember(oThdConf, "affine_to_cpu");
|
||||
|
||||
if(mode == nullptr || no_prefetch == nullptr || aff == nullptr)
|
||||
return false;
|
||||
|
||||
if(!mode->IsBool() || !no_prefetch->IsBool())
|
||||
return false;
|
||||
|
||||
if(!aff->IsNumber() && !aff->IsBool())
|
||||
return false;
|
||||
|
||||
if(aff->IsNumber() && aff->GetInt64() < 0)
|
||||
return false;
|
||||
|
||||
cfg.bDoubleMode = mode->GetBool();
|
||||
cfg.bNoPrefetch = no_prefetch->GetBool();
|
||||
|
||||
if(aff->IsNumber())
|
||||
cfg.iCpuAff = aff->GetInt64();
|
||||
else
|
||||
cfg.iCpuAff = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
jconf::slow_mem_cfg jconf::GetSlowMemSetting()
|
||||
{
|
||||
const char* opt = prv->configValues[sUseSlowMem]->GetString();
|
||||
|
||||
if(strcasecmp(opt, "always") == 0)
|
||||
return always_use;
|
||||
else if(strcasecmp(opt, "no_mlck") == 0)
|
||||
return no_mlck;
|
||||
else if(strcasecmp(opt, "warn") == 0)
|
||||
return print_warning;
|
||||
else if(strcasecmp(opt, "never") == 0)
|
||||
return never_use;
|
||||
else
|
||||
return unknown_value;
|
||||
}
|
||||
|
||||
bool jconf::GetTlsSetting()
|
||||
{
|
||||
return prv->configValues[bTlsMode]->GetBool();
|
||||
@@ -205,19 +144,6 @@ bool jconf::PreferIpv4()
|
||||
return prv->configValues[bPreferIpv4]->GetBool();
|
||||
}
|
||||
|
||||
size_t jconf::GetThreadCount()
|
||||
{
|
||||
if(prv->configValues[aCpuThreadsConf]->IsArray())
|
||||
return prv->configValues[aCpuThreadsConf]->Size();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool jconf::NeedsAutoconf()
|
||||
{
|
||||
return !prv->configValues[aCpuThreadsConf]->IsArray();
|
||||
}
|
||||
|
||||
uint64_t jconf::GetCallTimeout()
|
||||
{
|
||||
return prv->configValues[iCallTimeout]->GetUint64();
|
||||
@@ -248,11 +174,6 @@ uint16_t jconf::GetHttpdPort()
|
||||
return prv->configValues[iHttpdPort]->GetUint();
|
||||
}
|
||||
|
||||
bool jconf::NiceHashMode()
|
||||
{
|
||||
return prv->configValues[bNiceHashMode]->GetBool();
|
||||
}
|
||||
|
||||
bool jconf::DaemonMode()
|
||||
{
|
||||
return prv->configValues[bDaemonMode]->GetBool();
|
||||
@@ -263,44 +184,12 @@ const char* jconf::GetOutputFile()
|
||||
return prv->configValues[sOutputFile]->GetString();
|
||||
}
|
||||
|
||||
void jconf::cpuid(uint32_t eax, int32_t ecx, int32_t val[4])
|
||||
{
|
||||
memset(val, 0, sizeof(int32_t)*4);
|
||||
|
||||
#ifdef _WIN32
|
||||
__cpuidex(val, eax, ecx);
|
||||
#else
|
||||
__cpuid_count(eax, ecx, val[0], val[1], val[2], val[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool jconf::check_cpu_features()
|
||||
{
|
||||
constexpr int AESNI_BIT = 1 << 25;
|
||||
constexpr int SSE2_BIT = 1 << 26;
|
||||
int32_t cpu_info[4];
|
||||
bool bHaveSse2;
|
||||
|
||||
cpuid(1, 0, cpu_info);
|
||||
|
||||
bHaveAes = (cpu_info[2] & AESNI_BIT) != 0;
|
||||
bHaveSse2 = (cpu_info[3] & SSE2_BIT) != 0;
|
||||
|
||||
return bHaveSse2;
|
||||
}
|
||||
|
||||
bool jconf::parse_config(const char* sFilename)
|
||||
{
|
||||
FILE * pFile;
|
||||
char * buffer;
|
||||
size_t flen;
|
||||
|
||||
if(!check_cpu_features())
|
||||
{
|
||||
printer::inst()->print_msg(L0, "CPU support of SSE2 is required.");
|
||||
return false;
|
||||
}
|
||||
|
||||
pFile = fopen(sFilename, "rb");
|
||||
if (pFile == NULL)
|
||||
{
|
||||
@@ -389,29 +278,6 @@ bool jconf::parse_config(const char* sFilename)
|
||||
}
|
||||
}
|
||||
|
||||
thd_cfg c;
|
||||
for(size_t i=0; i < GetThreadCount(); i++)
|
||||
{
|
||||
if(!GetThreadConfig(i, c))
|
||||
{
|
||||
printer::inst()->print_msg(L0, "Thread %llu has invalid config.", int_port(i));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(NiceHashMode() && GetThreadCount() >= 32)
|
||||
{
|
||||
printer::inst()->print_msg(L0, "You need to use less than 32 threads in NiceHash mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(GetSlowMemSetting() == unknown_value)
|
||||
{
|
||||
printer::inst()->print_msg(L0,
|
||||
"Invalid config file. use_slow_memory must be \"always\", \"no_mlck\", \"warn\" or \"never\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!prv->configValues[iCallTimeout]->IsUint64() ||
|
||||
!prv->configValues[iNetRetry]->IsUint64() ||
|
||||
!prv->configValues[iGiveUpLimit]->IsUint64())
|
||||
@@ -454,14 +320,5 @@ bool jconf::parse_config(const char* sFilename)
|
||||
|
||||
printer::inst()->set_verbose_level(prv->configValues[iVerboseLevel]->GetUint64());
|
||||
|
||||
if(NeedsAutoconf())
|
||||
return true;
|
||||
|
||||
if(prv->configValues[bAesOverride]->IsBool())
|
||||
bHaveAes = prv->configValues[bAesOverride]->GetBool();
|
||||
|
||||
if(!bHaveAes)
|
||||
printer::inst()->print_msg(L0, "Your CPU doesn't support hardware AES. Don't expect high hashrates.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,12 +27,6 @@ public:
|
||||
unknown_value
|
||||
};
|
||||
|
||||
size_t GetThreadCount();
|
||||
bool GetThreadConfig(size_t id, thd_cfg &cfg);
|
||||
bool NeedsAutoconf();
|
||||
|
||||
slow_mem_cfg GetSlowMemSetting();
|
||||
|
||||
bool GetTlsSetting();
|
||||
bool TlsSecureAlgos();
|
||||
const char* GetTlsFingerprint();
|
||||
@@ -52,15 +46,10 @@ public:
|
||||
|
||||
uint16_t GetHttpdPort();
|
||||
|
||||
bool NiceHashMode();
|
||||
|
||||
bool DaemonMode();
|
||||
|
||||
bool PreferIpv4();
|
||||
|
||||
inline bool HaveHardwareAes() { return bHaveAes; }
|
||||
|
||||
static void cpuid(uint32_t eax, int32_t ecx, int32_t val[4]);
|
||||
|
||||
private:
|
||||
jconf();
|
||||
|
||||
@@ -394,7 +394,10 @@ bool jpsock::process_pool_job(const opq_json_val* params)
|
||||
if(!hex2bin(sTempStr, 8, (unsigned char*)&iTempInt) || iTempInt == 0)
|
||||
return set_socket_error("PARSE error: Invalid target");
|
||||
|
||||
|
||||
oPoolJob.iTarget = t32_to_t64(iTempInt);
|
||||
oPoolJob.iTarget32 = iTempInt;
|
||||
|
||||
}
|
||||
else if(target_slen <= 16)
|
||||
{
|
||||
|
||||
-555
@@ -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);
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include "crypto/cryptonight.h"
|
||||
|
||||
class telemetry
|
||||
{
|
||||
public:
|
||||
telemetry(size_t iThd);
|
||||
void push_perf_value(size_t iThd, uint64_t iHashCount, uint64_t iTimestamp);
|
||||
double calc_telemetry_data(size_t iLastMilisec, size_t iThread);
|
||||
|
||||
private:
|
||||
constexpr static size_t iBucketSize = 2 << 11; //Power of 2 to simplify calculations
|
||||
constexpr static size_t iBucketMask = iBucketSize - 1;
|
||||
uint32_t* iBucketTop;
|
||||
uint64_t** ppHashCounts;
|
||||
uint64_t** ppTimestamps;
|
||||
};
|
||||
|
||||
class minethd
|
||||
{
|
||||
public:
|
||||
struct miner_work
|
||||
{
|
||||
char sJobID[64];
|
||||
uint8_t bWorkBlob[112];
|
||||
uint32_t iWorkSize;
|
||||
uint32_t iResumeCnt;
|
||||
uint64_t iTarget;
|
||||
bool bNiceHash;
|
||||
bool bStall;
|
||||
size_t iPoolId;
|
||||
|
||||
miner_work() : iWorkSize(0), bStall(true), iPoolId(0) { }
|
||||
|
||||
miner_work(const char* sJobID, const uint8_t* bWork, uint32_t iWorkSize, uint32_t iResumeCnt,
|
||||
uint64_t iTarget, bool bNiceHash, size_t iPoolId) : iWorkSize(iWorkSize), iResumeCnt(iResumeCnt),
|
||||
iTarget(iTarget), bNiceHash(bNiceHash), bStall(false), iPoolId(iPoolId)
|
||||
{
|
||||
assert(iWorkSize <= sizeof(bWorkBlob));
|
||||
memcpy(this->sJobID, sJobID, sizeof(miner_work::sJobID));
|
||||
memcpy(this->bWorkBlob, bWork, iWorkSize);
|
||||
}
|
||||
|
||||
miner_work(miner_work const&) = delete;
|
||||
|
||||
miner_work& operator=(miner_work const& from)
|
||||
{
|
||||
assert(this != &from);
|
||||
|
||||
iWorkSize = from.iWorkSize;
|
||||
iResumeCnt = from.iResumeCnt;
|
||||
iTarget = from.iTarget;
|
||||
bNiceHash = from.bNiceHash;
|
||||
bStall = from.bStall;
|
||||
iPoolId = from.iPoolId;
|
||||
|
||||
assert(iWorkSize <= sizeof(bWorkBlob));
|
||||
memcpy(sJobID, from.sJobID, sizeof(sJobID));
|
||||
memcpy(bWorkBlob, from.bWorkBlob, iWorkSize);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
miner_work(miner_work&& from) : iWorkSize(from.iWorkSize), iTarget(from.iTarget),
|
||||
bStall(from.bStall), iPoolId(from.iPoolId)
|
||||
{
|
||||
assert(iWorkSize <= sizeof(bWorkBlob));
|
||||
memcpy(sJobID, from.sJobID, sizeof(sJobID));
|
||||
memcpy(bWorkBlob, from.bWorkBlob, iWorkSize);
|
||||
}
|
||||
|
||||
miner_work& operator=(miner_work&& from)
|
||||
{
|
||||
assert(this != &from);
|
||||
|
||||
iWorkSize = from.iWorkSize;
|
||||
iResumeCnt = from.iResumeCnt;
|
||||
iTarget = from.iTarget;
|
||||
bNiceHash = from.bNiceHash;
|
||||
bStall = from.bStall;
|
||||
iPoolId = from.iPoolId;
|
||||
|
||||
assert(iWorkSize <= sizeof(bWorkBlob));
|
||||
memcpy(sJobID, from.sJobID, sizeof(sJobID));
|
||||
memcpy(bWorkBlob, from.bWorkBlob, iWorkSize);
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
static void switch_work(miner_work& pWork);
|
||||
static std::vector<minethd*>* thread_starter(miner_work& pWork);
|
||||
static bool self_test();
|
||||
|
||||
std::atomic<uint64_t> iHashCount;
|
||||
std::atomic<uint64_t> iTimestamp;
|
||||
|
||||
private:
|
||||
typedef void (*cn_hash_fun)(const void*, size_t, void*, cryptonight_ctx*);
|
||||
typedef void (*cn_hash_fun_dbl)(const void*, size_t, void*, cryptonight_ctx* __restrict, cryptonight_ctx* __restrict);
|
||||
|
||||
minethd(miner_work& pWork, size_t iNo, bool double_work, bool no_prefetch, int64_t affinity);
|
||||
|
||||
// We use the top 10 bits of the nonce for thread and resume
|
||||
// This allows us to resume up to 128 threads 4 times before
|
||||
// we get nonce collisions
|
||||
// Bottom 22 bits allow for an hour of work at 1000 H/s
|
||||
inline uint32_t calc_start_nonce(uint32_t resume)
|
||||
{ return (resume * iThreadCount + iThreadNo) << 22; }
|
||||
|
||||
// Limited version of the nonce calc above
|
||||
inline uint32_t calc_nicehash_nonce(uint32_t start, uint32_t resume)
|
||||
{ return start | (resume * iThreadCount + iThreadNo) << 18; }
|
||||
|
||||
static cn_hash_fun func_selector(bool bHaveAes, bool bNoPrefetch);
|
||||
static cn_hash_fun_dbl func_dbl_selector(bool bHaveAes, bool bNoPrefetch);
|
||||
|
||||
void work_main();
|
||||
void double_work_main();
|
||||
void consume_work();
|
||||
|
||||
static std::atomic<uint64_t> iGlobalJobNo;
|
||||
static std::atomic<uint64_t> iConsumeCnt;
|
||||
static uint64_t iThreadCount;
|
||||
uint64_t iJobNo;
|
||||
|
||||
static miner_work oGlobalWork;
|
||||
miner_work oWork;
|
||||
|
||||
void pin_thd_affinity();
|
||||
// Held by the creating context to prevent a race cond with oWorkThd = std::thread(...)
|
||||
std::mutex work_thd_mtx;
|
||||
|
||||
std::thread oWorkThd;
|
||||
uint8_t iThreadNo;
|
||||
int64_t affinity;
|
||||
|
||||
bool bQuit;
|
||||
bool bNoPrefetch;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,8 @@ struct pool_job
|
||||
char sJobID[64];
|
||||
uint8_t bWorkBlob[112];
|
||||
uint64_t iTarget;
|
||||
// \todo remove workaround needed for amd
|
||||
uint32_t iTarget32;
|
||||
uint32_t iWorkLen;
|
||||
uint32_t iResumeCnt;
|
||||
|
||||
@@ -38,6 +40,7 @@ struct job_result
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum ex_event_name { EV_INVALID_VAL, EV_SOCK_READY, EV_SOCK_ERROR,
|
||||
EV_POOL_HAVE_JOB, EV_MINER_HAVE_RESULT, EV_PERF_TICK, EV_RECONNECT,
|
||||
EV_SWITCH_POOL, EV_DEV_POOL_EXIT, EV_USR_HASHRATE, EV_USR_RESULTS, EV_USR_CONNSTAT,
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 <cmath>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include "telemetry.h"
|
||||
|
||||
namespace xmrstak
|
||||
{
|
||||
|
||||
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[i], 0, sizeof(uint64_t) * iBucketSize);
|
||||
memset(ppTimestamps[i], 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;
|
||||
}
|
||||
|
||||
} // namepsace xmrstak
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xmrstak
|
||||
{
|
||||
|
||||
class telemetry
|
||||
{
|
||||
public:
|
||||
telemetry(size_t iThd);
|
||||
void push_perf_value(size_t iThd, uint64_t iHashCount, uint64_t iTimestamp);
|
||||
double calc_telemetry_data(size_t iLastMilisec, size_t iThread);
|
||||
|
||||
private:
|
||||
constexpr static size_t iBucketSize = 2 << 11; //Power of 2 to simplify calculations
|
||||
constexpr static size_t iBucketMask = iBucketSize - 1;
|
||||
uint32_t* iBucketTop;
|
||||
uint64_t** ppHashCounts;
|
||||
uint64_t** ppTimestamps;
|
||||
};
|
||||
|
||||
} // namepsace xmrstak
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define XMR_STAK_NAME "xmr-stak-cpu"
|
||||
#define XMR_STAK_VERSION "1.3.0-1.5.0-dev"
|
||||
#define XMR_STAK_NAME "xmr-stak"
|
||||
#define XMR_STAK_VERSION "2.0.0-predev"
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ extern const char sHtmlCommonHeader [] =
|
||||
extern const char sHtmlHashrateBodyHigh [] =
|
||||
"<div class=data>"
|
||||
"<table>"
|
||||
"<tr><th>Thread ID</th><th>2.5s</th><th>60s</th><th>15m</th><th rowspan='%u'>H/s</td></tr>";
|
||||
"<tr><th>Thread ID</th><th>10s</th><th>60s</th><th>15m</th><th rowspan='%u'>H/s</td></tr>";
|
||||
|
||||
extern const char sHtmlHashrateTableRow [] =
|
||||
"<tr><th>%u</th><td>%s</td><td>%s</td><td>%s</td></tr>";
|
||||
|
||||
Reference in New Issue
Block a user