diff --git a/CMakeLists.txt b/CMakeLists.txt index 18e5a06e6..753ee492e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ set(HEADERS src/Summary.h src/workers/Handle.h src/workers/SingleWorker.h + src/workers/Telemetry.h src/workers/Worker.h src/workers/Workers.h src/version.h @@ -52,6 +53,7 @@ set(SOURCES src/Summary.cpp src/workers/Handle.cpp src/workers/SingleWorker.cpp + src/workers/Telemetry.cpp src/workers/Worker.cpp src/workers/Workers.cpp src/xmrig.cpp @@ -86,7 +88,7 @@ endif() add_definitions(/D_GNU_SOURCE) add_definitions(/DUNICODE) -add_definitions(/DAPP_DEBUG) +#add_definitions(/DAPP_DEBUG) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") diff --git a/src/interfaces/IClientListener.h b/src/interfaces/IClientListener.h index 73a855161..570190359 100644 --- a/src/interfaces/IClientListener.h +++ b/src/interfaces/IClientListener.h @@ -34,10 +34,10 @@ class IClientListener public: virtual ~IClientListener() {} - virtual void onClose(Client *client, int failures); + virtual void onClose(Client *client, int failures) = 0; virtual void onJobReceived(Client *client, const Job &job) = 0; - virtual void onLoginCredentialsRequired(Client *client) = 0; - virtual void onLoginSuccess(Client *client) = 0; + virtual void onLoginCredentialsRequired(Client *client) = 0; + virtual void onLoginSuccess(Client *client) = 0; }; diff --git a/src/interfaces/IWorker.h b/src/interfaces/IWorker.h index f9010a2f3..b9b6eb0a0 100644 --- a/src/interfaces/IWorker.h +++ b/src/interfaces/IWorker.h @@ -25,12 +25,17 @@ #define __IWORKER_H__ +#include + + class IWorker { public: virtual ~IWorker() {} - virtual void start() = 0; + virtual uint64_t hashCount() const = 0; + virtual uint64_t timestamp() const = 0; + virtual void start() = 0; }; diff --git a/src/workers/Handle.h b/src/workers/Handle.h index a8ad84c19..6b2b1d37c 100644 --- a/src/workers/Handle.h +++ b/src/workers/Handle.h @@ -42,6 +42,7 @@ public: inline int threadId() const { return m_threadId; } inline int threads() const { return m_threads; } inline int64_t affinity() const { return m_affinity; } + inline IWorker *worker() const { return m_worker; } inline void setWorker(IWorker *worker) { m_worker = worker; } private: diff --git a/src/workers/SingleWorker.cpp b/src/workers/SingleWorker.cpp index c102029c4..263f08595 100644 --- a/src/workers/SingleWorker.cpp +++ b/src/workers/SingleWorker.cpp @@ -51,6 +51,10 @@ void SingleWorker::start() } while (!Workers::isOutdated(m_sequence)) { + if ((m_count & 0xF) == 0) { + storeStats(); + } + m_count++; *m_job.nonce() = ++m_result.nonce; diff --git a/src/workers/Telemetry.cpp b/src/workers/Telemetry.cpp new file mode 100644 index 000000000..b1a36633e --- /dev/null +++ b/src/workers/Telemetry.cpp @@ -0,0 +1,107 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * 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 + * (at your option) 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 . + */ + + +#include +#include +#include + +#include "Console.h" +#include "workers/Telemetry.h" + + +Telemetry::Telemetry(int threads) : + m_threads(threads) +{ + m_counts = new uint64_t*[threads]; + m_timestamps = new uint64_t*[threads]; + m_top = new uint32_t[threads]; + + for (int i = 0; i < threads; i++) { + m_counts[i] = new uint64_t[kBucketSize]; + m_timestamps[i] = new uint64_t[kBucketSize]; + m_top[i] = 0; + + memset(m_counts[0], 0, sizeof(uint64_t) * kBucketSize); + memset(m_timestamps[0], 0, sizeof(uint64_t) * kBucketSize); + } +} + + +double Telemetry::calc(size_t threadId, size_t ms) const +{ + using namespace std::chrono; + const uint64_t now = time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); + + uint64_t earliestHashCount = 0; + uint64_t earliestStamp = 0; + uint64_t lastestStamp = 0; + uint64_t lastestHashCnt = 0; + bool haveFullSet = false; + + for (size_t i = 1; i < kBucketSize; i++) { + const size_t idx = (m_top[threadId] - i) & kBucketMask; + + if (m_timestamps[threadId][idx] == 0) { + break; + } + + if (lastestStamp == 0) { + lastestStamp = m_timestamps[threadId][idx]; + lastestHashCnt = m_counts[threadId][idx]; + } + + if (now - m_timestamps[threadId][idx] > ms) { + haveFullSet = true; + break; + } + + earliestStamp = m_timestamps[threadId][idx]; + earliestHashCount = m_counts[threadId][idx]; + } + + if (!haveFullSet || earliestStamp == 0 || lastestStamp == 0) { + return nan(""); + } + + if (lastestStamp - earliestStamp == 0) { + return nan(""); + } + + double hashes, time; + hashes = lastestHashCnt - earliestHashCount; + time = lastestStamp - earliestStamp; + time /= 1000.0; + + return hashes / time; +} + + +void Telemetry::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; + + m_top[threadId] = (top + 1) & kBucketMask; +} diff --git a/src/workers/Telemetry.h b/src/workers/Telemetry.h new file mode 100644 index 000000000..007ba536e --- /dev/null +++ b/src/workers/Telemetry.h @@ -0,0 +1,49 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * 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 + * (at your option) 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 . + */ + +#ifndef __TELEMETRY_H__ +#define __TELEMETRY_H__ + + +#include + + +class Telemetry +{ +public: + Telemetry(int threads); + double calc(size_t threadId, size_t ms) const; + void add(size_t threadId, uint64_t count, uint64_t timestamp); + +private: + constexpr static size_t kBucketSize = 2 << 11; + constexpr static size_t kBucketMask = kBucketSize - 1; + + int m_threads; + uint32_t* m_top; + uint64_t** m_counts; + uint64_t** m_timestamps; +}; + + +#endif /* __TELEMETRY_H__ */ diff --git a/src/workers/Worker.cpp b/src/workers/Worker.cpp index d39ebdc32..5478bcf78 100644 --- a/src/workers/Worker.cpp +++ b/src/workers/Worker.cpp @@ -21,6 +21,9 @@ * along with this program. If not, see . */ +#include + +#include "Console.h" #include "Cpu.h" #include "Mem.h" @@ -32,6 +35,8 @@ Worker::Worker(Handle *handle) : m_nicehash(handle->nicehash()), m_id(handle->threadId()), m_threads(handle->threads()), + m_hashCount(0), + m_timestamp(0), m_count(0), m_sequence(0) { @@ -48,3 +53,13 @@ Worker::Worker(Handle *handle) : Worker::~Worker() { } + + +void Worker::storeStats() +{ + using namespace std::chrono; + + const uint64_t timestamp = time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); + m_hashCount.store(m_count, std::memory_order_relaxed); + m_timestamp.store(timestamp, std::memory_order_relaxed); +} diff --git a/src/workers/Worker.h b/src/workers/Worker.h index 35b55b908..84f3ccf87 100644 --- a/src/workers/Worker.h +++ b/src/workers/Worker.h @@ -25,6 +25,7 @@ #define __WORKER_H__ +#include #include @@ -41,11 +42,18 @@ public: Worker(Handle *handle); ~Worker(); + inline uint64_t hashCount() const override { return m_hashCount.load(std::memory_order_relaxed); } + inline uint64_t timestamp() const override { return m_timestamp.load(std::memory_order_relaxed); } + protected: + void storeStats(); + bool m_nicehash; cryptonight_ctx *m_ctx; int m_id; int m_threads; + std::atomic m_hashCount; + std::atomic m_timestamp; uint64_t m_count; uint64_t m_sequence; }; diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp index f3a26cd00..832d7da00 100644 --- a/src/workers/Workers.cpp +++ b/src/workers/Workers.cpp @@ -21,9 +21,14 @@ * along with this program. If not, see . */ +#include + + +#include "Console.h" #include "interfaces/IJobResultListener.h" #include "workers/Handle.h" #include "workers/SingleWorker.h" +#include "workers/Telemetry.h" #include "workers/Workers.h" @@ -35,7 +40,10 @@ std::atomic Workers::m_paused; std::atomic Workers::m_sequence; std::list Workers::m_queue; std::vector Workers::m_workers; +Telemetry *Workers::m_telemetry = nullptr; +uint64_t Workers::m_ticks = 0; uv_async_t Workers::m_async; +uv_timer_t Workers::m_timer; Job Workers::job() @@ -61,6 +69,8 @@ void Workers::setJob(const Job &job) void Workers::start(int threads, int64_t affinity, bool nicehash) { + m_telemetry = new Telemetry(threads); + pthread_mutex_init(&m_mutex, nullptr); pthread_rwlock_init(&m_rwlock, nullptr); @@ -68,6 +78,8 @@ void Workers::start(int threads, int64_t affinity, bool nicehash) m_paused = 1; uv_async_init(uv_default_loop(), &m_async, Workers::onResult); + uv_timer_init(uv_default_loop(), &m_timer); + uv_timer_start(&m_timer, Workers::onPerfTick, 500, 500); for (int i = 0; i < threads; ++i) { Handle *handle = new Handle(i, threads, affinity, nicehash); @@ -98,6 +110,34 @@ void *Workers::onReady(void *arg) } +void Workers::onPerfTick(uv_timer_t *handle) +{ + for (Handle *handle : m_workers) { + m_telemetry->add(handle->threadId(), handle->worker()->hashCount(), handle->worker()->timestamp()); + } + + if ((m_ticks++ & 0xF) == 0) { + double hps = 0.0; + double telem; + bool normal = true; + + for (Handle *handle : m_workers) { + telem = m_telemetry->calc(handle->threadId(), 2500); + if (!std::isnormal(telem)) { + normal = false; + break; + } + else { + hps += telem; + } + } + + if (normal) { + LOG_NOTICE("%03.1f H/s", hps); + } + } +} + void Workers::onResult(uv_async_t *handle) { diff --git a/src/workers/Workers.h b/src/workers/Workers.h index 93913b4ba..f7a0e43a8 100644 --- a/src/workers/Workers.h +++ b/src/workers/Workers.h @@ -37,6 +37,7 @@ class Handle; class IJobResultListener; +class Telemetry; class Workers @@ -55,6 +56,7 @@ public: private: static void *onReady(void *arg); + static void onPerfTick(uv_timer_t *handle); static void onResult(uv_async_t *handle); static IJobResultListener *m_listener; @@ -65,7 +67,10 @@ private: static std::atomic m_sequence; static std::list m_queue; static std::vector m_workers; + static Telemetry *m_telemetry; + static uint64_t m_ticks; static uv_async_t m_async; + static uv_timer_t m_timer; };