Files
mines/zano/libethcore/Miner.h

486 lines
12 KiB
C++

/*
This file is part of progminer.
progminer 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.
progminer 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 progminer. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <bitset>
#include <list>
#include <numeric>
#include <string>
#include "EthashAux.h"
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libdevcore/Worker.h>
#include <boost/asio.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#define DAG_LOAD_MODE_PARALLEL 0
#define DAG_LOAD_MODE_SEQUENTIAL 1
using namespace std;
extern boost::asio::io_service g_io_service;
namespace dev
{
namespace eth
{
enum class DeviceTypeEnum
{
Unknown,
Cpu,
Gpu,
Accelerator
};
enum class DeviceSubscriptionTypeEnum
{
None,
OpenCL,
Cuda,
Cpu
};
enum class MinerType
{
Mixed,
CL,
CUDA,
CPU
};
enum class HwMonitorInfoType
{
UNKNOWN,
NVIDIA,
AMD,
CPU
};
enum class ClPlatformTypeEnum
{
Unknown,
Amd,
Clover,
Nvidia
};
enum class SolutionAccountingEnum
{
Accepted,
Rejected,
Wasted,
Failed
};
struct MinerSettings
{
vector<unsigned> devices;
};
// Holds settings for CUDA Miner
struct CUSettings : public MinerSettings
{
unsigned streams = 2;
unsigned schedule = 4;
unsigned gridSize = 256;
unsigned blockSize = 512;
unsigned parallelHash = 4;
};
// Holds settings for OpenCL Miner
struct CLSettings : public MinerSettings
{
bool noBinary = false;
unsigned globalWorkSize = 0;
unsigned globalWorkSizeMultiplier = 32768;
unsigned localWorkSize = 256;
};
// Holds settings for CPU Miner
struct CPSettings : public MinerSettings
{
};
struct SolutionAccountType
{
unsigned accepted = 0;
unsigned rejected = 0;
unsigned wasted = 0;
unsigned failed = 0;
std::chrono::steady_clock::time_point tstamp = std::chrono::steady_clock::now();
string str()
{
string _ret = "A" + to_string(accepted);
if (wasted)
_ret.append(":W" + to_string(wasted));
if (rejected)
_ret.append(":R" + to_string(rejected));
if (failed)
_ret.append(":F" + to_string(failed));
return _ret;
};
};
struct HwSensorsType
{
int tempC = 0;
int fanP = 0;
double powerW = 0.0;
string str()
{
string _ret = to_string(tempC) + "C " + to_string(fanP) + "%";
if (powerW)
_ret.append(boost::str(boost::format("%f") % powerW));
return _ret;
};
};
struct TelemetryAccountType
{
string prefix = "";
float hashrate = 0.0f;
bool paused = false;
HwSensorsType sensors;
SolutionAccountType solutions;
};
struct DeviceDescriptor
{
DeviceTypeEnum type = DeviceTypeEnum::Unknown;
DeviceSubscriptionTypeEnum subscriptionType = DeviceSubscriptionTypeEnum::None;
string uniqueId; // For GPUs this is the PCI ID
size_t totalMemory; // Total memory available by device
string name; // Device Name
bool clDetected; // For OpenCL detected devices
string clName;
unsigned int clPlatformId;
string clPlatformName;
ClPlatformTypeEnum clPlatformType = ClPlatformTypeEnum::Unknown;
string clPlatformVersion;
unsigned int clPlatformVersionMajor;
unsigned int clPlatformVersionMinor;
unsigned int clDeviceOrdinal;
unsigned int clDeviceIndex;
string clDeviceVersion;
unsigned int clDeviceVersionMajor;
unsigned int clDeviceVersionMinor;
string clBoardName;
size_t clMaxMemAlloc;
size_t clMaxWorkGroup;
unsigned int clMaxComputeUnits;
string clNvCompute;
unsigned int clNvComputeMajor;
unsigned int clNvComputeMinor;
bool cuDetected; // For CUDA detected devices
string cuName;
unsigned int cuDeviceOrdinal;
unsigned int cuDeviceIndex;
string cuCompute;
unsigned int cuComputeMajor;
unsigned int cuComputeMinor;
int cpCpuNumer; // For CPU
};
struct HwMonitorInfo
{
HwMonitorInfoType deviceType = HwMonitorInfoType::UNKNOWN;
string devicePciId;
int deviceIndex = -1;
};
/// Pause mining
enum MinerPauseEnum
{
PauseDueToOverHeating,
PauseDueToAPIRequest,
PauseDueToFarmPaused,
PauseDueToInsufficientMemory,
PauseDueToInitEpochError,
Pause_MAX // Must always be last as a placeholder of max count
};
/// Keeps track of progress for farm and miners
struct TelemetryType
{
bool hwmon = false;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
TelemetryAccountType farm;
std::vector<TelemetryAccountType> miners;
std::string str()
{
std::stringstream _ret;
/*
Output is formatted as
Run <h:mm> <Solutions> <Speed> [<miner> ...]
where
- Run h:mm Duration of the batch
- Solutions Detailed solutions (A+R+F) per farm
- Speed Actual hashing rate
each <miner> reports
- speed Actual speed at the same level of
magnitude for farm speed
- sensors Values of sensors (temp, fan, power)
- solutions Optional (LOG_PER_GPU) Solutions detail per GPU
*/
/*
Calculate duration
*/
auto duration = std::chrono::steady_clock::now() - start;
auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
int hoursSize = (hours.count() > 9 ? (hours.count() > 99 ? 3 : 2) : 1);
duration -= hours;
auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
_ret << EthGreen << setw(hoursSize) << hours.count() << ":" << setfill('0') << setw(2)
<< minutes.count() << EthReset << EthWhiteBold << " " << farm.solutions.str()
<< EthReset << " ";
/*
Github : @AndreaLanfranchi
I whish I could simply make use of getFormattedHashes but in this case
this would be misleading as total hashrate could be of a different order
of magnitude than the hashrate expressed by single devices.
Thus I need to set the vary same scaling index on the farm and on devices
*/
static string suffixes[] = {"h", "Kh", "Mh", "Gh"};
float hr = farm.hashrate;
int magnitude = 0;
while (hr > 1000.0f && magnitude <= 3)
{
hr /= 1000.0f;
magnitude++;
}
_ret << EthTealBold << std::fixed << std::setprecision(2) << hr << " "
<< suffixes[magnitude] << EthReset << " - ";
int i = -1; // Current miner index
int m = miners.size() - 1; // Max miner index
for (TelemetryAccountType miner : miners)
{
i++;
hr = miner.hashrate;
if (hr > 0.0f)
hr /= pow(1000.0f, magnitude);
_ret << (miner.paused ? EthRed : "") << miner.prefix << i << " " << EthTeal
<< std::fixed << std::setprecision(2) << hr << EthReset;
if (hwmon)
_ret << " " << EthTeal << miner.sensors.str() << EthReset;
// Eventually push also solutions per single GPU
if (g_logOptions & LOG_PER_GPU)
_ret << " " << EthTeal << miner.solutions.str() << EthReset;
// Separator if not the last miner index
if (i < m)
_ret << ", ";
}
return _ret.str();
};
};
/**
* @brief Class for hosting one or more Miners.
* @warning Must be implemented in a threadsafe manner since it will be called from multiple
* miner threads.
*/
class FarmFace
{
public:
FarmFace() { m_this = this; }
static FarmFace& f() { return *m_this; };
virtual ~FarmFace() = default;
virtual unsigned get_tstart() = 0;
virtual unsigned get_tstop() = 0;
virtual unsigned get_ergodicity() = 0;
/**
* @brief Called from a Miner to note a WorkPackage has a solution.
* @param _p The solution.
* @return true iff the solution was good (implying that mining should be .
*/
virtual void submitProof(Solution const& _p) = 0;
virtual void accountSolution(unsigned _minerIdx, SolutionAccountingEnum _accounting) = 0;
virtual uint64_t get_nonce_scrambler() = 0;
virtual unsigned get_segment_width() = 0;
private:
static FarmFace* m_this;
};
/**
* @brief A miner - a member and adoptee of the Farm.
* @warning Not threadsafe. It is assumed Farm will synchronise calls to/from this class.
*/
class Miner : public Worker
{
public:
Miner(std::string const& _name, unsigned _index)
: Worker(_name + std::to_string(_index)), m_index(_index)
{}
~Miner() override = default;
// Sets basic info for eventual serialization of DAG load
static void setDagLoadInfo(unsigned _mode, unsigned _devicecount)
{
s_dagLoadMode = _mode;
s_dagLoadIndex = 0;
s_minersCount = _devicecount;
};
/**
* @brief Gets the device descriptor assigned to this instance
*/
DeviceDescriptor getDescriptor();
/**
* @brief Assigns hashing work to this instance
*/
void setWork(WorkPackage const& _work);
/**
* @brief Assigns Epoch context to this instance
*/
void setEpoch(EpochContext const& _ec) { m_epochContext = _ec; }
unsigned Index() { return m_index; };
HwMonitorInfo hwmonInfo() { return m_hwmoninfo; }
void setHwmonDeviceIndex(int i) { m_hwmoninfo.deviceIndex = i; }
/**
* @brief Kick an asleep miner.
*/
virtual void kick_miner() = 0;
/**
* @brief Pauses mining setting a reason flag
*/
void pause(MinerPauseEnum what);
/**
* @brief Whether or not this miner is paused for any reason
*/
bool paused();
/**
* @brief Checks if the given reason for pausing is currently active
*/
bool pauseTest(MinerPauseEnum what);
/**
* @brief Returns the human readable reason for this miner being paused
*/
std::string pausedString();
/**
* @brief Cancels a pause flag.
* @note Miner can be paused for multiple reasons at a time.
*/
void resume(MinerPauseEnum fromwhat);
/**
* @brief Retrieves currrently collected hashrate
*/
float RetrieveHashRate() noexcept;
void TriggerHashRateUpdate() noexcept;
protected:
/**
* @brief Initializes miner's device.
*/
virtual bool initDevice() = 0;
/**
* @brief Initializes miner to current (or changed) epoch.
*/
bool initEpoch();
/**
* @brief Miner's specific initialization to current (or changed) epoch.
*/
virtual bool initEpoch_internal() = 0;
/**
* @brief Returns current workpackage this miner is working on
*/
WorkPackage work() const;
void updateHashRate(uint32_t _groupSize, uint32_t _increment) noexcept;
bool dropThreadPriority();
static unsigned s_minersCount; // Total Number of Miners
static unsigned s_dagLoadMode; // Way dag should be loaded
static unsigned s_dagLoadIndex; // In case of serialized load of dag this is the index of miner
// which should load next
const unsigned m_index = 0; // Ordinal index of the Instance (not the device)
DeviceDescriptor m_deviceDescriptor; // Info about the device
EpochContext m_epochContext;
#ifdef DEV_BUILD
std::chrono::steady_clock::time_point m_workSwitchStart;
#endif
HwMonitorInfo m_hwmoninfo;
mutable boost::mutex x_work;
mutable boost::mutex x_pause;
boost::condition_variable m_new_work_signal;
boost::condition_variable m_dag_loaded_signal;
uint64_t m_nextProgpowPeriod = 0;
boost::thread* m_compileThread = nullptr;
private:
bitset<MinerPauseEnum::Pause_MAX> m_pauseFlags;
WorkPackage m_work;
std::chrono::steady_clock::time_point m_hashTime = std::chrono::steady_clock::now();
std::atomic<float> m_hashRate = {0.0};
uint64_t m_groupCount = 0;
atomic<bool> m_hashRateUpdate = {false};
};
} // namespace eth
} // namespace dev