update rot files

- add config file editor
- move telemetry out of minethd
This commit is contained in:
psychocrypt
2017-09-30 23:46:08 +02:00
parent 652b79f684
commit 7d88cf456b
19 changed files with 374 additions and 1440 deletions
+57
View File
@@ -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
View File
@@ -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;
};
-195
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2
View File
@@ -41,3 +41,5 @@ private:
verbosity verbose_level;
FILE* logfile;
};
void win_exit();
+32 -15
View File
@@ -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
View File
@@ -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;
+1 -144
View File
@@ -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;
}
-11
View File
@@ -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();
+3
View File
@@ -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
View File
@@ -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);
}
-144
View File
@@ -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;
};
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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>";