Hashrate: display numerical error
Different workers store stats asynchronously and with different frequency. It has direct impact on accuracy of hashrate calculations. Error calculation overhead balanced with faster iteration due to tail index - Hashrate is being calculated as previously - Error is being displayed when it's above zero
This commit is contained in:
parent
1fdc8631e3
commit
9826039373
3 changed files with 107 additions and 72 deletions
|
@ -34,12 +34,18 @@
|
|||
#include "base/io/json/Json.h"
|
||||
#include "base/tools/Chrono.h"
|
||||
#include "base/tools/Handle.h"
|
||||
#include "base/io/log/Log.h"
|
||||
|
||||
|
||||
inline static const char *format(double h, char *buf, size_t size)
|
||||
inline static const char *format(double h, char *buf, size_t size, double errorDown, double errorUp)
|
||||
{
|
||||
if (std::isnormal(h)) {
|
||||
snprintf(buf, size, (h < 100.0) ? "%04.2f" : "%03.1f", h);
|
||||
if (std::max(errorDown, errorUp) >= (h < 100.0 ? 0.01 : 0.1)) {
|
||||
snprintf(buf, size, (h < 100.0) ? "%5.2f" BLACK_BOLD("(-%.2f/+%.2f}") : "%5.1f" BLACK_BOLD("(-%.1f/+%.1f)"), h, errorDown, errorUp);
|
||||
}
|
||||
else {
|
||||
snprintf(buf, size, (h < 100.0) ? "%5.2f" : "%5.1f", h);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -52,12 +58,16 @@ xmrig::Hashrate::Hashrate(size_t threads) :
|
|||
{
|
||||
m_counts = new uint64_t*[threads];
|
||||
m_timestamps = new uint64_t*[threads];
|
||||
m_top = new uint32_t[threads];
|
||||
m_head = new uint32_t[threads];
|
||||
m_tail = new uint32_t*[threads];
|
||||
|
||||
const uint64_t now = xmrig::Chrono::highResolutionMSecs();
|
||||
for (size_t i = 0; i < threads; i++) {
|
||||
m_counts[i] = new uint64_t[kBucketSize]();
|
||||
m_timestamps[i] = new uint64_t[kBucketSize]();
|
||||
m_top[i] = 0;
|
||||
m_head[i] = 0;
|
||||
m_tail[i] = new uint32_t[3]();
|
||||
m_timestamps[i][0] = now;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,92 +77,97 @@ xmrig::Hashrate::~Hashrate()
|
|||
for (size_t i = 0; i < m_threads; i++) {
|
||||
delete [] m_counts[i];
|
||||
delete [] m_timestamps[i];
|
||||
delete [] m_tail[i];
|
||||
}
|
||||
|
||||
delete [] m_counts;
|
||||
delete [] m_timestamps;
|
||||
delete [] m_top;
|
||||
delete [] m_head;
|
||||
delete [] m_tail;
|
||||
}
|
||||
|
||||
|
||||
double xmrig::Hashrate::calc(size_t ms) const
|
||||
|
||||
xmrig::Hashrate::Value xmrig::Hashrate::calc(size_t threadId, Intervals ms) const
|
||||
{
|
||||
TimeRange time;
|
||||
uint64_t count;
|
||||
if (!findRecords(threadId, ms, time, count)) {
|
||||
return { nan(""), nan(""), nan("") };
|
||||
}
|
||||
return { count * 1000.0 / (time.second - time.first), 0.0, 0.0 };
|
||||
}
|
||||
|
||||
xmrig::Hashrate::Value xmrig::Hashrate::calc(Intervals ms) const
|
||||
{
|
||||
const uint64_t now = xmrig::Chrono::highResolutionMSecs();
|
||||
uint64_t time_earliest[2] = { now, 0 };
|
||||
uint64_t time_latest[2] = { 0, now };
|
||||
uint64_t total_count = 0;
|
||||
double result = 0.0;
|
||||
double data;
|
||||
|
||||
for (size_t i = 0; i < m_threads; ++i) {
|
||||
data = calc(i, ms);
|
||||
if (std::isnormal(data)) {
|
||||
result += data;
|
||||
for (size_t threadId = 0; threadId < m_threads; ++threadId) {
|
||||
TimeRange time;
|
||||
uint64_t count;
|
||||
if (!findRecords(threadId, ms, time, count)) {
|
||||
continue;
|
||||
}
|
||||
for (size_t j = 0; j < 2; ++j) {
|
||||
if ((time.first < time_earliest[j]) ^ j) {
|
||||
time_earliest[j] = time.first;
|
||||
}
|
||||
if ((time.second > time_latest[j]) ^ j) {
|
||||
time_latest[j] = time.second;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
total_count += count;
|
||||
result += count * 1000.0 / (time.second - time.first);
|
||||
}
|
||||
const double lower = total_count * 1000.0 / (time_latest[0] - time_earliest[0]);
|
||||
const double upper = total_count * 1000.0 / (time_latest[1] - time_earliest[1]);
|
||||
return { result, result - lower, upper - result };
|
||||
}
|
||||
|
||||
|
||||
double xmrig::Hashrate::calc(size_t threadId, size_t ms) const
|
||||
bool xmrig::Hashrate::findRecords(size_t threadId, Intervals ms, TimeRange &time, uint64_t &count) const
|
||||
{
|
||||
assert(threadId < m_threads);
|
||||
if (threadId >= m_threads) {
|
||||
return nan("");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t earliestHashCount = 0;
|
||||
uint64_t earliestStamp = 0;
|
||||
bool haveFullSet = false;
|
||||
|
||||
const uint64_t timeStampLimit = xmrig::Chrono::highResolutionMSecs() - ms;
|
||||
uint64_t* timestamps = m_timestamps[threadId];
|
||||
uint64_t* counts = m_counts[threadId];
|
||||
|
||||
const size_t idx_start = (m_top[threadId] - 1) & kBucketMask;
|
||||
size_t idx = idx_start;
|
||||
|
||||
uint64_t lastestStamp = timestamps[idx];
|
||||
uint64_t lastestHashCnt = counts[idx];
|
||||
|
||||
do {
|
||||
if (timestamps[idx] < timeStampLimit) {
|
||||
haveFullSet = (timestamps[idx] != 0);
|
||||
if (idx != idx_start) {
|
||||
idx = (idx + 1) & kBucketMask;
|
||||
earliestStamp = timestamps[idx];
|
||||
earliestHashCount = counts[idx];
|
||||
const uint32_t head = m_head[threadId];
|
||||
// time[tale] < timeStampLimit <= time[later_tail] <= time[head]
|
||||
uint32_t &tail = m_tail[threadId][ms == ShortInterval ? 0 : (ms == MediumInterval ? 1 : 2)];
|
||||
if (m_timestamps[threadId][tail] >= timeStampLimit) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
while (tail != head) {
|
||||
const uint32_t later_tail = (tail + 1) & kBucketMask;
|
||||
if (m_timestamps[threadId][later_tail] >= timeStampLimit) {
|
||||
time = { m_timestamps[threadId][later_tail], m_timestamps[threadId][head] };
|
||||
count = m_counts[threadId][head] - m_counts[threadId][later_tail];
|
||||
return true;
|
||||
}
|
||||
idx = (idx - 1) & kBucketMask;
|
||||
} while (idx != idx_start);
|
||||
|
||||
if (!haveFullSet || earliestStamp == 0 || lastestStamp == 0) {
|
||||
return nan("");
|
||||
tail = later_tail;
|
||||
}
|
||||
|
||||
if (lastestStamp - earliestStamp == 0) {
|
||||
return nan("");
|
||||
}
|
||||
|
||||
const auto hashes = static_cast<double>(lastestHashCnt - earliestHashCount);
|
||||
const auto time = static_cast<double>(lastestStamp - earliestStamp) / 1000.0;
|
||||
|
||||
return hashes / time;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Hashrate::add(size_t threadId, uint64_t count, uint64_t timestamp)
|
||||
{
|
||||
const size_t top = m_top[threadId];
|
||||
m_counts[threadId][top] = count;
|
||||
m_timestamps[threadId][top] = timestamp;
|
||||
const uint32_t head = (m_head[threadId] + 1) & kBucketMask;
|
||||
m_counts[threadId][head] = count;
|
||||
m_timestamps[threadId][head] = timestamp;
|
||||
|
||||
m_top[threadId] = (top + 1) & kBucketMask;
|
||||
m_head[threadId] = head;
|
||||
}
|
||||
|
||||
|
||||
const char *xmrig::Hashrate::format(double h, char *buf, size_t size)
|
||||
const char *xmrig::Hashrate::format(Value h, char *buf, size_t size)
|
||||
{
|
||||
return ::format(h, buf, size);
|
||||
return ::format(h.estimate, buf, size, h.errorDown, h.errorUp);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
|
||||
#include "3rdparty/rapidjson/fwd.h"
|
||||
|
@ -42,6 +43,21 @@ class Hashrate
|
|||
public:
|
||||
XMRIG_DISABLE_COPY_MOVE_DEFAULT(Hashrate)
|
||||
|
||||
struct Value {
|
||||
Value() = default;
|
||||
Value(const double estimate, const double errorDown, const double errorUp): estimate(estimate), errorDown(errorDown), errorUp(errorUp) {}
|
||||
|
||||
inline operator double() const { return estimate; }
|
||||
inline Value operator *(const double v) const { return { estimate * v , errorDown * v, errorUp * v}; }
|
||||
inline Value& operator +=(const Value &o) { estimate += o.estimate; errorDown += o.errorDown; errorUp += o.errorUp; return *this; }
|
||||
|
||||
double estimate;
|
||||
double errorDown;
|
||||
double errorUp;
|
||||
};
|
||||
|
||||
typedef std::pair<uint64_t, uint64_t> TimeRange;
|
||||
|
||||
enum Intervals {
|
||||
ShortInterval = 10000,
|
||||
MediumInterval = 60000,
|
||||
|
@ -50,13 +66,13 @@ public:
|
|||
|
||||
Hashrate(size_t threads);
|
||||
~Hashrate();
|
||||
double calc(size_t ms) const;
|
||||
double calc(size_t threadId, size_t ms) const;
|
||||
Value calc(Intervals ms) const;
|
||||
Value calc(size_t threadId, Intervals ms) const;
|
||||
void add(size_t threadId, uint64_t count, uint64_t timestamp);
|
||||
|
||||
inline size_t threads() const { return m_threads; }
|
||||
|
||||
static const char *format(double h, char *buf, size_t size);
|
||||
static const char *format(Value h, char *buf, size_t size);
|
||||
static rapidjson::Value normalize(double d);
|
||||
|
||||
# ifdef XMRIG_FEATURE_API
|
||||
|
@ -65,11 +81,14 @@ public:
|
|||
# endif
|
||||
|
||||
private:
|
||||
bool findRecords(size_t threadId, Intervals ms, TimeRange &time, uint64_t &count) const;
|
||||
|
||||
constexpr static size_t kBucketSize = 2 << 11;
|
||||
constexpr static size_t kBucketMask = kBucketSize - 1;
|
||||
|
||||
size_t m_threads;
|
||||
uint32_t* m_top;
|
||||
uint32_t* m_head;
|
||||
uint32_t** m_tail;
|
||||
uint64_t** m_counts;
|
||||
uint64_t** m_timestamps;
|
||||
};
|
||||
|
|
|
@ -244,8 +244,9 @@ public:
|
|||
|
||||
void printHashrate(bool details)
|
||||
{
|
||||
char num[16 * 4] = { 0 };
|
||||
double speed[3] = { 0.0 };
|
||||
constexpr size_t num_width = 32;
|
||||
char num[num_width * 4] = {};
|
||||
Hashrate::Value speed[3] = {};
|
||||
|
||||
for (auto backend : backends) {
|
||||
const auto hashrate = backend->hashrate();
|
||||
|
@ -304,12 +305,12 @@ public:
|
|||
}
|
||||
# endif
|
||||
|
||||
LOG_INFO("%s " WHITE_BOLD("speed") " 10s/60s/15m " CYAN_BOLD("%s") CYAN(" %s %s ") CYAN_BOLD("%s") " max " CYAN_BOLD("%s %s"),
|
||||
LOG_INFO("%s " WHITE_BOLD("speed") " 10s/60s/15m " CYAN_BOLD("%s") " " CYAN("%s") " " CYAN("%s") " " CYAN_BOLD("%s") " max " CYAN_BOLD("%s %s"),
|
||||
Tags::miner(),
|
||||
Hashrate::format(speed[0] * scale, num, sizeof(num) / 4),
|
||||
Hashrate::format(speed[1] * scale, num + 16, sizeof(num) / 4),
|
||||
Hashrate::format(speed[2] * scale, num + 16 * 2, sizeof(num) / 4), h,
|
||||
Hashrate::format(maxHashrate[algorithm] * scale, num + 16 * 3, sizeof(num) / 4), h
|
||||
Hashrate::format(speed[0] * scale, num, num_width),
|
||||
Hashrate::format(speed[1] * scale, num + num_width, num_width),
|
||||
Hashrate::format(speed[2] * scale, num + num_width, num_width), h,
|
||||
Hashrate::format(maxHashrate[algorithm] * scale, num + num_width * 3, num_width), h
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -327,7 +328,7 @@ public:
|
|||
bool battery_power = false;
|
||||
Controller *controller;
|
||||
Job job;
|
||||
mutable std::map<Algorithm::Id, double> maxHashrate;
|
||||
mutable std::map<Algorithm::Id, Hashrate::Value> maxHashrate;
|
||||
std::vector<IBackend *> backends;
|
||||
String userJobId;
|
||||
Timer *timer = nullptr;
|
||||
|
@ -570,7 +571,7 @@ void xmrig::Miner::onConfigChanged(Config *config, Config *previousConfig)
|
|||
|
||||
void xmrig::Miner::onTimer(const Timer *)
|
||||
{
|
||||
double maxHashrate = 0.0;
|
||||
Hashrate::Value maxHashrate{};
|
||||
const auto healthPrintTime = d_ptr->controller->config()->healthPrintTime();
|
||||
|
||||
for (IBackend *backend : d_ptr->backends) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue