progminer zano miner fork https://github.com/hyle-team/progminer
This commit is contained in:
20
zano/libethcore/CMakeLists.txt
Normal file
20
zano/libethcore/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
set(SOURCES
|
||||
EthashAux.h EthashAux.cpp
|
||||
Farm.cpp Farm.h
|
||||
Miner.h Miner.cpp
|
||||
)
|
||||
|
||||
include_directories(BEFORE ..)
|
||||
|
||||
add_library(ethcore ${SOURCES})
|
||||
target_link_libraries(ethcore PUBLIC devcore ethash PRIVATE hwmon)
|
||||
|
||||
if(ETHASHCL)
|
||||
target_link_libraries(ethcore PRIVATE ethash-cl)
|
||||
endif()
|
||||
if(ETHASHCUDA)
|
||||
target_link_libraries(ethcore PUBLIC ethash-cuda)
|
||||
endif()
|
||||
if(ETHASHCPU)
|
||||
target_link_libraries(ethcore PUBLIC ethash-cpu)
|
||||
endif()
|
||||
44
zano/libethcore/EthashAux.cpp
Normal file
44
zano/libethcore/EthashAux.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include "EthashAux.h"
|
||||
|
||||
#include <ethash/ethash.hpp>
|
||||
#include <ethash/progpow.hpp>
|
||||
|
||||
using namespace dev;
|
||||
using namespace eth;
|
||||
|
||||
Result EthashAux::eval(int epoch, h256 const& _headerHash, uint64_t _nonce) noexcept
|
||||
{
|
||||
auto headerHash = ethash::hash256_from_bytes(_headerHash.data());
|
||||
auto& context = ethash::get_global_epoch_context(epoch);
|
||||
auto result = ethash::hash(context, headerHash, _nonce);
|
||||
h256 mix{reinterpret_cast<byte*>(result.mix_hash.bytes), h256::ConstructFromPointer};
|
||||
h256 final{reinterpret_cast<byte*>(result.final_hash.bytes), h256::ConstructFromPointer};
|
||||
return {final, mix};
|
||||
}
|
||||
|
||||
Result EthashAux::eval(int epoch, int _block_number, h256 const& _headerHash, uint64_t _nonce) noexcept
|
||||
{
|
||||
auto headerHash = ethash::hash256_from_bytes(_headerHash.data());
|
||||
auto& context = ethash::get_global_epoch_context(epoch);
|
||||
auto result = progpow::hash(context, _block_number, headerHash, _nonce);
|
||||
h256 mix{reinterpret_cast<byte*>(result.mix_hash.bytes), h256::ConstructFromPointer};
|
||||
h256 final{reinterpret_cast<byte*>(result.final_hash.bytes), h256::ConstructFromPointer};
|
||||
return {final, mix};
|
||||
}
|
||||
84
zano/libethcore/EthashAux.h
Normal file
84
zano/libethcore/EthashAux.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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 <libdevcore/Common.h>
|
||||
#include <libdevcore/Exceptions.h>
|
||||
#include <libdevcore/Worker.h>
|
||||
|
||||
#include <ethash/ethash.hpp>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace eth
|
||||
{
|
||||
struct Result
|
||||
{
|
||||
h256 value;
|
||||
h256 mixHash;
|
||||
};
|
||||
|
||||
class EthashAux
|
||||
{
|
||||
public:
|
||||
static Result eval(int epoch, h256 const& _headerHash, uint64_t _nonce) noexcept;
|
||||
static Result eval(int epoch, int _block_number, h256 const& _headerHash, uint64_t _nonce) noexcept;
|
||||
};
|
||||
|
||||
struct EpochContext
|
||||
{
|
||||
int epochNumber;
|
||||
int lightNumItems;
|
||||
size_t lightSize;
|
||||
const ethash_hash512* lightCache;
|
||||
int dagNumItems;
|
||||
uint64_t dagSize;
|
||||
};
|
||||
|
||||
struct WorkPackage
|
||||
{
|
||||
WorkPackage() = default;
|
||||
|
||||
explicit operator bool() const { return header != h256(); }
|
||||
|
||||
std::string job; // Job identifier can be anything. Not necessarily a hash
|
||||
|
||||
h256 boundary;
|
||||
h256 header; ///< When h256() means "pause until notified a new work package is available".
|
||||
h256 seed;
|
||||
|
||||
int epoch = -1;
|
||||
int block = -1;
|
||||
|
||||
uint64_t startNonce = 0;
|
||||
uint16_t exSizeBytes = 0;
|
||||
|
||||
std::string algo = "ethash";
|
||||
};
|
||||
|
||||
struct Solution
|
||||
{
|
||||
uint64_t nonce; // Solution found nonce
|
||||
h256 mixHash; // Mix hash
|
||||
WorkPackage work; // WorkPackage this solution refers to
|
||||
std::chrono::steady_clock::time_point tstamp; // Timestamp of found solution
|
||||
unsigned midx; // Originating miner Id
|
||||
};
|
||||
|
||||
} // namespace eth
|
||||
} // namespace dev
|
||||
686
zano/libethcore/Farm.cpp
Normal file
686
zano/libethcore/Farm.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <libethcore/Farm.h>
|
||||
|
||||
#if ETH_ETHASHCL
|
||||
#include <libethash-cl/CLMiner.h>
|
||||
#endif
|
||||
|
||||
#if ETH_ETHASHCUDA
|
||||
#include <libethash-cuda/CUDAMiner.h>
|
||||
#endif
|
||||
|
||||
#if ETH_ETHASHCPU
|
||||
#include <libethash-cpu/CPUMiner.h>
|
||||
#endif
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace eth
|
||||
{
|
||||
Farm* Farm::m_this = nullptr;
|
||||
|
||||
Farm::Farm(std::map<std::string, DeviceDescriptor>& _DevicesCollection,
|
||||
FarmSettings _settings, CUSettings _CUSettings, CLSettings _CLSettings, CPSettings _CPSettings)
|
||||
: m_Settings(std::move(_settings)),
|
||||
m_CUSettings(std::move(_CUSettings)),
|
||||
m_CLSettings(std::move(_CLSettings)),
|
||||
m_CPSettings(std::move(_CPSettings)),
|
||||
m_io_strand(g_io_service),
|
||||
m_collectTimer(g_io_service),
|
||||
m_DevicesCollection(_DevicesCollection)
|
||||
{
|
||||
m_this = this;
|
||||
|
||||
// Init HWMON if needed
|
||||
if (m_Settings.hwMon)
|
||||
{
|
||||
m_telemetry.hwmon = true;
|
||||
|
||||
#if defined(__linux)
|
||||
bool need_sysfsh = false;
|
||||
#else
|
||||
bool need_adlh = false;
|
||||
#endif
|
||||
bool need_nvmlh = false;
|
||||
|
||||
// Scan devices collection to identify which hw monitors to initialize
|
||||
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
|
||||
{
|
||||
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cuda)
|
||||
{
|
||||
need_nvmlh = true;
|
||||
continue;
|
||||
}
|
||||
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::OpenCL)
|
||||
{
|
||||
if (it->second.clPlatformType == ClPlatformTypeEnum::Nvidia)
|
||||
{
|
||||
need_nvmlh = true;
|
||||
continue;
|
||||
}
|
||||
if (it->second.clPlatformType == ClPlatformTypeEnum::Amd)
|
||||
{
|
||||
#if defined(__linux)
|
||||
need_sysfsh = true;
|
||||
#else
|
||||
need_adlh = true;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__linux)
|
||||
if (need_sysfsh)
|
||||
sysfsh = wrap_amdsysfs_create();
|
||||
if (sysfsh)
|
||||
{
|
||||
// Build Pci identification mapping as done in miners.
|
||||
for (int i = 0; i < sysfsh->sysfs_gpucount; i++)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
std::string uniqueId;
|
||||
oss << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< (unsigned int)sysfsh->sysfs_pci_bus_id[i] << ":" << std::setw(2)
|
||||
<< (unsigned int)(sysfsh->sysfs_pci_device_id[i]) << ".0";
|
||||
uniqueId = oss.str();
|
||||
map_amdsysfs_handle[uniqueId] = i;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
if (need_adlh)
|
||||
adlh = wrap_adl_create();
|
||||
if (adlh)
|
||||
{
|
||||
// Build Pci identification as done in miners.
|
||||
for (int i = 0; i < adlh->adl_gpucount; i++)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
std::string uniqueId;
|
||||
oss << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< (unsigned int)adlh->devs[adlh->phys_logi_device_id[i]].iBusNumber << ":"
|
||||
<< std::setw(2)
|
||||
<< (unsigned int)(adlh->devs[adlh->phys_logi_device_id[i]].iDeviceNumber)
|
||||
<< ".0";
|
||||
uniqueId = oss.str();
|
||||
map_adl_handle[uniqueId] = i;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
if (need_nvmlh)
|
||||
nvmlh = wrap_nvml_create();
|
||||
if (nvmlh)
|
||||
{
|
||||
// Build Pci identification as done in miners.
|
||||
for (int i = 0; i < nvmlh->nvml_gpucount; i++)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
std::string uniqueId;
|
||||
oss << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< (unsigned int)nvmlh->nvml_pci_bus_id[i] << ":" << std::setw(2)
|
||||
<< (unsigned int)(nvmlh->nvml_pci_device_id[i] >> 3) << ".0";
|
||||
uniqueId = oss.str();
|
||||
map_nvml_handle[uniqueId] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize nonce_scrambler
|
||||
shuffle();
|
||||
|
||||
// Start data collector timer
|
||||
// It should work for the whole lifetime of Farm
|
||||
// regardless it's mining state
|
||||
m_collectTimer.expires_from_now(boost::posix_time::milliseconds(m_collectInterval));
|
||||
m_collectTimer.async_wait(
|
||||
m_io_strand.wrap(boost::bind(&Farm::collectData, this, boost::asio::placeholders::error)));
|
||||
}
|
||||
|
||||
Farm::~Farm()
|
||||
{
|
||||
// Stop data collector (before monitors !!!)
|
||||
m_collectTimer.cancel();
|
||||
|
||||
// Deinit HWMON
|
||||
#if defined(__linux)
|
||||
if (sysfsh)
|
||||
wrap_amdsysfs_destroy(sysfsh);
|
||||
#else
|
||||
if (adlh)
|
||||
wrap_adl_destroy(adlh);
|
||||
#endif
|
||||
if (nvmlh)
|
||||
wrap_nvml_destroy(nvmlh);
|
||||
|
||||
// Stop mining (if needed)
|
||||
if (m_isMining.load(std::memory_order_relaxed))
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Randomizes the nonce scrambler
|
||||
*/
|
||||
void Farm::shuffle()
|
||||
{
|
||||
// Given that all nonces are equally likely to solve the problem
|
||||
// we could reasonably always start the nonce search ranges
|
||||
// at a fixed place, but that would be boring. Provide a once
|
||||
// per run randomized start place, without creating much overhead.
|
||||
random_device engine;
|
||||
m_nonce_scrambler = uniform_int_distribution<uint64_t>()(engine);
|
||||
}
|
||||
|
||||
void Farm::setWork(WorkPackage const& _newWp)
|
||||
{
|
||||
// Set work to each miner giving it's own starting nonce
|
||||
Guard l(x_minerWork);
|
||||
|
||||
// Retrieve appropriate EpochContext
|
||||
if (m_currentWp.epoch != _newWp.epoch)
|
||||
{
|
||||
ethash::epoch_context _ec = ethash::get_global_epoch_context(_newWp.epoch);
|
||||
m_currentEc.epochNumber = _newWp.epoch;
|
||||
m_currentEc.lightNumItems = _ec.light_cache_num_items;
|
||||
m_currentEc.lightSize = ethash::get_light_cache_size(_ec.light_cache_num_items);
|
||||
m_currentEc.dagNumItems = ethash::calculate_full_dataset_num_items(_newWp.epoch);
|
||||
m_currentEc.dagSize = ethash::get_full_dataset_size(m_currentEc.dagNumItems);
|
||||
m_currentEc.lightCache = _ec.light_cache;
|
||||
|
||||
for (auto const& miner : m_miners)
|
||||
miner->setEpoch(m_currentEc);
|
||||
}
|
||||
|
||||
m_currentWp = _newWp;
|
||||
|
||||
// Check if we need to shuffle per work (ergodicity == 2)
|
||||
if (m_Settings.ergodicity == 2 && m_currentWp.exSizeBytes == 0)
|
||||
shuffle();
|
||||
|
||||
uint64_t _startNonce;
|
||||
if (m_currentWp.exSizeBytes > 0)
|
||||
{
|
||||
// Equally divide the residual segment among miners
|
||||
_startNonce = m_currentWp.startNonce;
|
||||
m_nonce_segment_with =
|
||||
(unsigned int)log2(pow(2, 64 - (m_currentWp.exSizeBytes * 4)) / m_miners.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the randomly selected nonce
|
||||
_startNonce = m_nonce_scrambler;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < m_miners.size(); i++)
|
||||
{
|
||||
m_currentWp.startNonce = _startNonce + ((uint64_t)i << m_nonce_segment_with);
|
||||
m_miners.at(i)->setWork(m_currentWp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start a number of miners.
|
||||
*/
|
||||
bool Farm::start()
|
||||
{
|
||||
// Prevent recursion
|
||||
if (m_isMining.load(std::memory_order_relaxed))
|
||||
return true;
|
||||
|
||||
Guard l(x_minerWork);
|
||||
|
||||
// Start all subscribed miners if none yet
|
||||
if (!m_miners.size())
|
||||
{
|
||||
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
|
||||
{
|
||||
TelemetryAccountType minerTelemetry;
|
||||
#if ETH_ETHASHCUDA
|
||||
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cuda)
|
||||
{
|
||||
minerTelemetry.prefix = "cu";
|
||||
m_miners.push_back(std::shared_ptr<Miner>(
|
||||
new CUDAMiner(m_miners.size(), m_CUSettings, it->second)));
|
||||
}
|
||||
#endif
|
||||
#if ETH_ETHASHCL
|
||||
|
||||
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::OpenCL)
|
||||
{
|
||||
minerTelemetry.prefix = "cl";
|
||||
m_miners.push_back(std::shared_ptr<Miner>(
|
||||
new CLMiner(m_miners.size(), m_CLSettings, it->second)));
|
||||
}
|
||||
#endif
|
||||
#if ETH_ETHASHCPU
|
||||
|
||||
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cpu)
|
||||
{
|
||||
minerTelemetry.prefix = "cp";
|
||||
m_miners.push_back(std::shared_ptr<Miner>(
|
||||
new CPUMiner(m_miners.size(), m_CPSettings, it->second)));
|
||||
}
|
||||
#endif
|
||||
if (minerTelemetry.prefix.empty())
|
||||
continue;
|
||||
m_telemetry.miners.push_back(minerTelemetry);
|
||||
m_miners.back()->startWorking();
|
||||
}
|
||||
|
||||
// Initialize DAG Load mode
|
||||
Miner::setDagLoadInfo(m_Settings.dagLoadMode, (unsigned int)m_miners.size());
|
||||
|
||||
m_isMining.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto const& miner : m_miners)
|
||||
miner->startWorking();
|
||||
m_isMining.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
return m_isMining.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop all mining activities.
|
||||
*/
|
||||
void Farm::stop()
|
||||
{
|
||||
// Avoid re-entering if not actually mining.
|
||||
// This, in fact, is also called by destructor
|
||||
if (isMining())
|
||||
{
|
||||
{
|
||||
Guard l(x_minerWork);
|
||||
for (auto const& miner : m_miners)
|
||||
{
|
||||
miner->triggerStopWorking();
|
||||
miner->kick_miner();
|
||||
}
|
||||
|
||||
m_miners.clear();
|
||||
m_isMining.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pauses the whole collection of miners
|
||||
*/
|
||||
void Farm::pause()
|
||||
{
|
||||
// Signal each miner to suspend mining
|
||||
Guard l(x_minerWork);
|
||||
m_paused.store(true, std::memory_order_relaxed);
|
||||
for (auto const& m : m_miners)
|
||||
m->pause(MinerPauseEnum::PauseDueToFarmPaused);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not this farm is paused for any reason
|
||||
*/
|
||||
bool Farm::paused()
|
||||
{
|
||||
return m_paused.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resumes from a pause condition
|
||||
*/
|
||||
void Farm::resume()
|
||||
{
|
||||
// Signal each miner to resume mining
|
||||
// Note ! Miners may stay suspended if other reasons
|
||||
Guard l(x_minerWork);
|
||||
m_paused.store(false, std::memory_order_relaxed);
|
||||
for (auto const& m : m_miners)
|
||||
m->resume(MinerPauseEnum::PauseDueToFarmPaused);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop all mining activities and Starts them again
|
||||
*/
|
||||
void Farm::restart()
|
||||
{
|
||||
if (m_onMinerRestart)
|
||||
m_onMinerRestart();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop all mining activities and Starts them again (async post)
|
||||
*/
|
||||
void Farm::restart_async()
|
||||
{
|
||||
g_io_service.post(m_io_strand.wrap(boost::bind(&Farm::restart, this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Spawn a reboot script (reboot.bat/reboot.sh)
|
||||
* @return false if no matching file was found
|
||||
*/
|
||||
bool Farm::reboot(const std::vector<std::string>& args)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
const char* filename = "reboot.bat";
|
||||
#else
|
||||
const char* filename = "reboot.sh";
|
||||
#endif
|
||||
|
||||
return spawn_file_in_bin_dir(filename, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Account solutions for miner and for farm
|
||||
*/
|
||||
void Farm::accountSolution(unsigned _minerIdx, SolutionAccountingEnum _accounting)
|
||||
{
|
||||
if (_accounting == SolutionAccountingEnum::Accepted)
|
||||
{
|
||||
m_telemetry.farm.solutions.accepted++;
|
||||
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
||||
m_telemetry.miners.at(_minerIdx).solutions.accepted++;
|
||||
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
||||
return;
|
||||
}
|
||||
if (_accounting == SolutionAccountingEnum::Wasted)
|
||||
{
|
||||
m_telemetry.farm.solutions.wasted++;
|
||||
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
||||
m_telemetry.miners.at(_minerIdx).solutions.wasted++;
|
||||
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
||||
return;
|
||||
}
|
||||
if (_accounting == SolutionAccountingEnum::Rejected)
|
||||
{
|
||||
m_telemetry.farm.solutions.rejected++;
|
||||
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
||||
m_telemetry.miners.at(_minerIdx).solutions.rejected++;
|
||||
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
||||
return;
|
||||
}
|
||||
if (_accounting == SolutionAccountingEnum::Failed)
|
||||
{
|
||||
m_telemetry.farm.solutions.failed++;
|
||||
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
||||
m_telemetry.miners.at(_minerIdx).solutions.failed++;
|
||||
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the solutions account for the whole farm
|
||||
*/
|
||||
|
||||
SolutionAccountType Farm::getSolutions()
|
||||
{
|
||||
return m_telemetry.farm.solutions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the solutions account for single miner
|
||||
*/
|
||||
SolutionAccountType Farm::getSolutions(unsigned _minerIdx)
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_telemetry.miners.at(_minerIdx).solutions;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return SolutionAccountType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides the description of segments each miner is working on
|
||||
* @return a JsonObject
|
||||
*/
|
||||
Json::Value Farm::get_nonce_scrambler_json()
|
||||
{
|
||||
Json::Value jRes;
|
||||
jRes["start_nonce"] = toHex(m_nonce_scrambler, HexPrefix::Add);
|
||||
jRes["device_width"] = m_nonce_segment_with;
|
||||
jRes["device_count"] = (uint64_t)m_miners.size();
|
||||
|
||||
return jRes;
|
||||
}
|
||||
|
||||
void Farm::setTStartTStop(unsigned tstart, unsigned tstop)
|
||||
{
|
||||
m_Settings.tempStart = tstart;
|
||||
m_Settings.tempStop = tstop;
|
||||
}
|
||||
|
||||
void Farm::submitProof(Solution const& _s)
|
||||
{
|
||||
g_io_service.post(m_io_strand.wrap(boost::bind(&Farm::submitProofAsync, this, _s)));
|
||||
}
|
||||
|
||||
void Farm::submitProofAsync(Solution const& _s)
|
||||
{
|
||||
#ifdef DEV_BUILD
|
||||
const bool dbuild = true;
|
||||
#else
|
||||
const bool dbuild = false;
|
||||
#endif
|
||||
if (!m_Settings.noEval || dbuild)
|
||||
{
|
||||
Result r = EthashAux::eval(_s.work.epoch, _s.work.block, _s.work.header, _s.nonce);
|
||||
if (r.value > _s.work.boundary)
|
||||
{
|
||||
accountSolution(_s.midx, SolutionAccountingEnum::Failed);
|
||||
cwarn << "GPU " << _s.midx
|
||||
<< " gave incorrect result. Lower overclocking values if it happens frequently.";
|
||||
return;
|
||||
}
|
||||
if (dbuild && (_s.mixHash != r.mixHash))
|
||||
cwarn << "GPU " << _s.midx << " mix missmatch";
|
||||
m_onSolutionFound(Solution{_s.nonce, r.mixHash, _s.work, _s.tstamp, _s.midx});
|
||||
}
|
||||
else
|
||||
m_onSolutionFound(_s);
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
if (g_logOptions & LOG_SUBMIT)
|
||||
cnote << "Submit time: "
|
||||
<< std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - _s.tstamp)
|
||||
.count()
|
||||
<< " us.";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Collects data about hashing and hardware status
|
||||
void Farm::collectData(const boost::system::error_code& ec)
|
||||
{
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
// Reset hashrate (it will accumulate from miners)
|
||||
float farm_hr = 0.0f;
|
||||
|
||||
// Process miners
|
||||
for (auto const& miner : m_miners)
|
||||
{
|
||||
int minerIdx = miner->Index();
|
||||
float hr = (miner->paused() ? 0.0f : miner->RetrieveHashRate());
|
||||
farm_hr += hr;
|
||||
m_telemetry.miners.at(minerIdx).hashrate = hr;
|
||||
m_telemetry.miners.at(minerIdx).paused = miner->paused();
|
||||
|
||||
|
||||
if (m_Settings.hwMon)
|
||||
{
|
||||
HwMonitorInfo hwInfo = miner->hwmonInfo();
|
||||
|
||||
unsigned int tempC = 0, fanpcnt = 0, powerW = 0;
|
||||
|
||||
if (hwInfo.deviceType == HwMonitorInfoType::NVIDIA && nvmlh)
|
||||
{
|
||||
int devIdx = hwInfo.deviceIndex;
|
||||
if (devIdx == -1 && !hwInfo.devicePciId.empty())
|
||||
{
|
||||
if (map_nvml_handle.find(hwInfo.devicePciId) != map_nvml_handle.end())
|
||||
{
|
||||
devIdx = map_nvml_handle[hwInfo.devicePciId];
|
||||
miner->setHwmonDeviceIndex(devIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will prevent further tries to map
|
||||
miner->setHwmonDeviceIndex(-2);
|
||||
}
|
||||
}
|
||||
|
||||
if (devIdx >= 0)
|
||||
{
|
||||
wrap_nvml_get_tempC(nvmlh, devIdx, &tempC);
|
||||
wrap_nvml_get_fanpcnt(nvmlh, devIdx, &fanpcnt);
|
||||
|
||||
if (m_Settings.hwMon == 2)
|
||||
wrap_nvml_get_power_usage(nvmlh, devIdx, &powerW);
|
||||
}
|
||||
}
|
||||
else if (hwInfo.deviceType == HwMonitorInfoType::AMD)
|
||||
{
|
||||
#if defined(__linux)
|
||||
if (sysfsh)
|
||||
{
|
||||
int devIdx = hwInfo.deviceIndex;
|
||||
if (devIdx == -1 && !hwInfo.devicePciId.empty())
|
||||
{
|
||||
if (map_amdsysfs_handle.find(hwInfo.devicePciId) !=
|
||||
map_amdsysfs_handle.end())
|
||||
{
|
||||
devIdx = map_amdsysfs_handle[hwInfo.devicePciId];
|
||||
miner->setHwmonDeviceIndex(devIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will prevent further tries to map
|
||||
miner->setHwmonDeviceIndex(-2);
|
||||
}
|
||||
}
|
||||
|
||||
if (devIdx >= 0)
|
||||
{
|
||||
wrap_amdsysfs_get_tempC(sysfsh, devIdx, &tempC);
|
||||
wrap_amdsysfs_get_fanpcnt(sysfsh, devIdx, &fanpcnt);
|
||||
|
||||
if (m_Settings.hwMon == 2)
|
||||
wrap_amdsysfs_get_power_usage(sysfsh, devIdx, &powerW);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (adlh) // Windows only for AMD
|
||||
{
|
||||
int devIdx = hwInfo.deviceIndex;
|
||||
if (devIdx == -1 && !hwInfo.devicePciId.empty())
|
||||
{
|
||||
if (map_adl_handle.find(hwInfo.devicePciId) != map_adl_handle.end())
|
||||
{
|
||||
devIdx = map_adl_handle[hwInfo.devicePciId];
|
||||
miner->setHwmonDeviceIndex(devIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will prevent further tries to map
|
||||
miner->setHwmonDeviceIndex(-2);
|
||||
}
|
||||
}
|
||||
|
||||
if (devIdx >= 0)
|
||||
{
|
||||
wrap_adl_get_tempC(adlh, devIdx, &tempC);
|
||||
wrap_adl_get_fanpcnt(adlh, devIdx, &fanpcnt);
|
||||
|
||||
if (m_Settings.hwMon == 2)
|
||||
wrap_adl_get_power_usage(adlh, devIdx, &powerW);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// If temperature control has been enabled call
|
||||
// check threshold
|
||||
if (m_Settings.tempStop)
|
||||
{
|
||||
bool paused = miner->pauseTest(MinerPauseEnum::PauseDueToOverHeating);
|
||||
if (!paused && (tempC >= m_Settings.tempStop))
|
||||
miner->pause(MinerPauseEnum::PauseDueToOverHeating);
|
||||
if (paused && (tempC <= m_Settings.tempStart))
|
||||
miner->resume(MinerPauseEnum::PauseDueToOverHeating);
|
||||
}
|
||||
|
||||
m_telemetry.miners.at(minerIdx).sensors.tempC = tempC;
|
||||
m_telemetry.miners.at(minerIdx).sensors.fanP = fanpcnt;
|
||||
m_telemetry.miners.at(minerIdx).sensors.powerW = powerW / ((double)1000.0);
|
||||
}
|
||||
m_telemetry.farm.hashrate = farm_hr;
|
||||
miner->TriggerHashRateUpdate();
|
||||
}
|
||||
|
||||
// Resubmit timer for another loop
|
||||
m_collectTimer.expires_from_now(boost::posix_time::milliseconds(m_collectInterval));
|
||||
m_collectTimer.async_wait(
|
||||
m_io_strand.wrap(boost::bind(&Farm::collectData, this, boost::asio::placeholders::error)));
|
||||
}
|
||||
|
||||
bool Farm::spawn_file_in_bin_dir(const char* filename, const std::vector<std::string>& args)
|
||||
{
|
||||
std::string fn = boost::dll::program_location().parent_path().string() +
|
||||
"/" + // boost::filesystem::path::preferred_separator
|
||||
filename;
|
||||
try
|
||||
{
|
||||
if (!boost::filesystem::exists(fn))
|
||||
return false;
|
||||
|
||||
/* anything in the file */
|
||||
if (!boost::filesystem::file_size(fn))
|
||||
return false;
|
||||
|
||||
#if defined(__linux)
|
||||
struct stat sb;
|
||||
if (stat(fn.c_str(), &sb) != 0)
|
||||
return false;
|
||||
/* just check if any exec flag is set.
|
||||
still execution can fail (not the uid, not in the group, selinux, ...)
|
||||
*/
|
||||
if ((sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
|
||||
return false;
|
||||
#endif
|
||||
/* spawn it (no wait,...) - fire and forget! */
|
||||
boost::process::spawn(fn, args);
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace eth
|
||||
} // namespace dev
|
||||
313
zano/libethcore/Farm.h
Normal file
313
zano/libethcore/Farm.h
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
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 <atomic>
|
||||
#include <list>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/dll.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/process.hpp>
|
||||
|
||||
#include <jsoncpp/json/json.h>
|
||||
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/Worker.h>
|
||||
|
||||
#include <libethcore/Miner.h>
|
||||
|
||||
#include <libhwmon/wrapnvml.h>
|
||||
#if defined(__linux)
|
||||
#include <libhwmon/wrapamdsysfs.h>
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#include <libhwmon/wrapadl.h>
|
||||
#endif
|
||||
|
||||
extern boost::asio::io_service g_io_service;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace eth
|
||||
{
|
||||
struct FarmSettings
|
||||
{
|
||||
unsigned dagLoadMode = 0; // 0 = Parallel; 1 = Serialized
|
||||
bool noEval = false; // Whether or not to re-evaluate solutions
|
||||
unsigned hwMon = 0; // 0 - No monitor; 1 - Temp and Fan; 2 - Temp Fan Power
|
||||
unsigned ergodicity = 0; // 0=default, 1=per session, 2=per job
|
||||
unsigned tempStart = 40; // Temperature threshold to restart mining (if paused)
|
||||
unsigned tempStop = 0; // Temperature threshold to pause mining (overheating)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A collective of Miners.
|
||||
* Miners ask for work, then submit proofs
|
||||
* @threadsafe
|
||||
*/
|
||||
class Farm : public FarmFace
|
||||
{
|
||||
public:
|
||||
unsigned tstart = 0, tstop = 0;
|
||||
|
||||
Farm(std::map<std::string, DeviceDescriptor>& _DevicesCollection,
|
||||
FarmSettings _settings, CUSettings _CUSettings, CLSettings _CLSettings,
|
||||
CPSettings _CPSettings);
|
||||
|
||||
~Farm();
|
||||
|
||||
static Farm& f() { return *m_this; }
|
||||
|
||||
/**
|
||||
* @brief Randomizes the nonce scrambler
|
||||
*/
|
||||
void shuffle();
|
||||
|
||||
/**
|
||||
* @brief Sets the current mining mission.
|
||||
* @param _wp The work package we wish to be mining.
|
||||
*/
|
||||
void setWork(WorkPackage const& _newWp);
|
||||
|
||||
/**
|
||||
* @brief Start a number of miners.
|
||||
*/
|
||||
bool start();
|
||||
|
||||
/**
|
||||
* @brief All mining activities to a full stop.
|
||||
* Implies all mining threads are stopped.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Signals all miners to suspend mining
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief Whether or not the whole farm has been paused
|
||||
*/
|
||||
bool paused();
|
||||
|
||||
/**
|
||||
* @brief Signals all miners to resume mining
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief Stop all mining activities and Starts them again
|
||||
*/
|
||||
void restart();
|
||||
|
||||
/**
|
||||
* @brief Stop all mining activities and Starts them again (async post)
|
||||
*/
|
||||
void restart_async();
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not the farm has been started
|
||||
*/
|
||||
bool isMining() const { return m_isMining.load(std::memory_order_relaxed); }
|
||||
|
||||
/**
|
||||
* @brief Spawn a reboot script (reboot.bat/reboot.sh)
|
||||
* @return false if no matching file was found
|
||||
*/
|
||||
bool reboot(const std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* @brief Get information on the progress of mining this work package.
|
||||
* @return The progress with mining so far.
|
||||
*/
|
||||
TelemetryType& Telemetry() { return m_telemetry; }
|
||||
|
||||
/**
|
||||
* @brief Gets current hashrate
|
||||
*/
|
||||
float HashRate() { return m_telemetry.farm.hashrate; };
|
||||
|
||||
/**
|
||||
* @brief Gets the collection of pointers to miner instances
|
||||
*/
|
||||
std::vector<std::shared_ptr<Miner>> getMiners() { return m_miners; }
|
||||
|
||||
/**
|
||||
* @brief Gets the number of miner instances
|
||||
*/
|
||||
unsigned getMinersCount() { return (unsigned)m_miners.size(); };
|
||||
|
||||
/**
|
||||
* @brief Gets the pointer to a miner instance
|
||||
*/
|
||||
std::shared_ptr<Miner> getMiner(unsigned index)
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_miners.at(index);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accounts a solution to a miner and, as a consequence, to
|
||||
* the whole farm
|
||||
*/
|
||||
void accountSolution(unsigned _minerIdx, SolutionAccountingEnum _accounting) override;
|
||||
|
||||
/**
|
||||
* @brief Gets the solutions account for the whole farm
|
||||
*/
|
||||
SolutionAccountType getSolutions();
|
||||
|
||||
/**
|
||||
* @brief Gets the solutions account for single miner
|
||||
*/
|
||||
SolutionAccountType getSolutions(unsigned _minerIdx);
|
||||
|
||||
using SolutionFound = std::function<void(const Solution&)>;
|
||||
using MinerRestart = std::function<void()>;
|
||||
|
||||
/**
|
||||
* @brief Provides a valid header based upon that received previously with setWork().
|
||||
* @param _bi The now-valid header.
|
||||
* @return true if the header was good and that the Farm should pause until more work is
|
||||
* submitted.
|
||||
*/
|
||||
void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; }
|
||||
|
||||
void onMinerRestart(MinerRestart const& _handler) { m_onMinerRestart = _handler; }
|
||||
|
||||
/**
|
||||
* @brief Gets the actual start nonce of the segment picked by the farm
|
||||
*/
|
||||
uint64_t get_nonce_scrambler() override { return m_nonce_scrambler; }
|
||||
|
||||
/**
|
||||
* @brief Gets the actual width of each subsegment assigned to miners
|
||||
*/
|
||||
unsigned get_segment_width() override { return m_nonce_segment_with; }
|
||||
|
||||
/**
|
||||
* @brief Sets the actual start nonce of the segment picked by the farm
|
||||
*/
|
||||
void set_nonce_scrambler(uint64_t n) { m_nonce_scrambler = n; }
|
||||
|
||||
/**
|
||||
* @brief Sets the actual width of each subsegment assigned to miners
|
||||
*/
|
||||
void set_nonce_segment_width(unsigned n)
|
||||
{
|
||||
if (!m_currentWp.exSizeBytes)
|
||||
m_nonce_segment_with = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides the description of segments each miner is working on
|
||||
* @return a JsonObject
|
||||
*/
|
||||
Json::Value get_nonce_scrambler_json();
|
||||
|
||||
void setTStartTStop(unsigned tstart, unsigned tstop);
|
||||
|
||||
unsigned get_tstart() override { return m_Settings.tempStart; }
|
||||
|
||||
unsigned get_tstop() override { return m_Settings.tempStop; }
|
||||
|
||||
unsigned get_ergodicity() override { return m_Settings.ergodicity; }
|
||||
|
||||
/**
|
||||
* @brief Called from a Miner to note a WorkPackage has a solution.
|
||||
* @param _s The solution.
|
||||
*/
|
||||
void submitProof(Solution const& _s) override;
|
||||
|
||||
bool getNoEval() { return m_Settings.noEval; }
|
||||
|
||||
private:
|
||||
std::atomic<bool> m_paused = {false};
|
||||
|
||||
// Async submits solution serializing execution
|
||||
// in Farm's strand
|
||||
void submitProofAsync(Solution const& _s);
|
||||
|
||||
// Collects data about hashing and hardware status
|
||||
void collectData(const boost::system::error_code& ec);
|
||||
|
||||
/**
|
||||
* @brief Spawn a file - must be located in the directory of progminer binary
|
||||
* @return false if file was not found or it is not executeable
|
||||
*/
|
||||
bool spawn_file_in_bin_dir(const char* filename, const std::vector<std::string>& args);
|
||||
|
||||
mutable Mutex x_minerWork;
|
||||
std::vector<std::shared_ptr<Miner>> m_miners; // Collection of miners
|
||||
|
||||
WorkPackage m_currentWp;
|
||||
EpochContext m_currentEc;
|
||||
|
||||
std::atomic<bool> m_isMining = {false};
|
||||
|
||||
TelemetryType m_telemetry; // Holds progress and status info for farm and miners
|
||||
|
||||
SolutionFound m_onSolutionFound;
|
||||
MinerRestart m_onMinerRestart;
|
||||
|
||||
FarmSettings m_Settings; // Own Farm Settings
|
||||
CUSettings m_CUSettings; // Cuda settings passed to CUDA Miner instantiator
|
||||
CLSettings m_CLSettings; // OpenCL settings passed to CL Miner instantiator
|
||||
CPSettings m_CPSettings; // CPU settings passed to CPU Miner instantiator
|
||||
|
||||
boost::asio::io_service::strand m_io_strand;
|
||||
boost::asio::deadline_timer m_collectTimer;
|
||||
static const int m_collectInterval = 5000;
|
||||
|
||||
string m_pool_addresses;
|
||||
|
||||
// StartNonce (non-NiceHash Mode) and
|
||||
// segment width assigned to each GPU as exponent of 2
|
||||
// considering an average block time of 15 seconds
|
||||
// a single device GPU should need a speed of 286 Mh/s
|
||||
// before it consumes the whole 2^32 segment
|
||||
uint64_t m_nonce_scrambler;
|
||||
unsigned int m_nonce_segment_with = 32;
|
||||
|
||||
// Wrappers for hardware monitoring libraries and their mappers
|
||||
wrap_nvml_handle* nvmlh = nullptr;
|
||||
std::map<string, int> map_nvml_handle = {};
|
||||
|
||||
#if defined(__linux)
|
||||
wrap_amdsysfs_handle* sysfsh = nullptr;
|
||||
std::map<string, int> map_amdsysfs_handle = {};
|
||||
#else
|
||||
wrap_adl_handle* adlh = nullptr;
|
||||
std::map<string, int> map_adl_handle = {};
|
||||
#endif
|
||||
|
||||
static Farm* m_this;
|
||||
std::map<std::string, DeviceDescriptor>& m_DevicesCollection;
|
||||
};
|
||||
|
||||
} // namespace eth
|
||||
} // namespace dev
|
||||
207
zano/libethcore/Miner.cpp
Normal file
207
zano/libethcore/Miner.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
This file is part of ethereum.
|
||||
|
||||
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.
|
||||
|
||||
ethereum 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/>.
|
||||
*/
|
||||
|
||||
#include "Miner.h"
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace eth
|
||||
{
|
||||
|
||||
unsigned Miner::s_dagLoadMode = 0;
|
||||
unsigned Miner::s_dagLoadIndex = 0;
|
||||
unsigned Miner::s_minersCount = 0;
|
||||
|
||||
FarmFace* FarmFace::m_this = nullptr;
|
||||
|
||||
DeviceDescriptor Miner::getDescriptor()
|
||||
{
|
||||
return m_deviceDescriptor;
|
||||
}
|
||||
|
||||
void Miner::setWork(WorkPackage const& _work)
|
||||
{
|
||||
{
|
||||
|
||||
boost::mutex::scoped_lock l(x_work);
|
||||
|
||||
// Void work if this miner is paused
|
||||
if (paused())
|
||||
m_work.header = h256();
|
||||
else
|
||||
m_work = _work;
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
m_workSwitchStart = std::chrono::steady_clock::now();
|
||||
#endif
|
||||
}
|
||||
|
||||
kick_miner();
|
||||
}
|
||||
|
||||
void Miner::pause(MinerPauseEnum what)
|
||||
{
|
||||
boost::mutex::scoped_lock l(x_pause);
|
||||
m_pauseFlags.set(what);
|
||||
m_work.header = h256();
|
||||
kick_miner();
|
||||
}
|
||||
|
||||
bool Miner::paused()
|
||||
{
|
||||
boost::mutex::scoped_lock l(x_pause);
|
||||
return m_pauseFlags.any();
|
||||
}
|
||||
|
||||
bool Miner::pauseTest(MinerPauseEnum what)
|
||||
{
|
||||
boost::mutex::scoped_lock l(x_pause);
|
||||
return m_pauseFlags.test(what);
|
||||
}
|
||||
|
||||
std::string Miner::pausedString()
|
||||
{
|
||||
boost::mutex::scoped_lock l(x_pause);
|
||||
std::string retVar;
|
||||
if (m_pauseFlags.any())
|
||||
{
|
||||
for (int i = 0; i < MinerPauseEnum::Pause_MAX; i++)
|
||||
{
|
||||
if (m_pauseFlags[(MinerPauseEnum)i])
|
||||
{
|
||||
if (!retVar.empty())
|
||||
retVar.append("; ");
|
||||
|
||||
if (i == MinerPauseEnum::PauseDueToOverHeating)
|
||||
retVar.append("Overheating");
|
||||
else if (i == MinerPauseEnum::PauseDueToAPIRequest)
|
||||
retVar.append("Api request");
|
||||
else if (i == MinerPauseEnum::PauseDueToFarmPaused)
|
||||
retVar.append("Farm suspended");
|
||||
else if (i == MinerPauseEnum::PauseDueToInsufficientMemory)
|
||||
retVar.append("Insufficient GPU memory");
|
||||
else if (i == MinerPauseEnum::PauseDueToInitEpochError)
|
||||
retVar.append("Epoch initialization error");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVar;
|
||||
}
|
||||
|
||||
void Miner::resume(MinerPauseEnum fromwhat)
|
||||
{
|
||||
boost::mutex::scoped_lock l(x_pause);
|
||||
m_pauseFlags.reset(fromwhat);
|
||||
//if (!m_pauseFlags.any())
|
||||
//{
|
||||
// // TODO Push most recent job from farm ?
|
||||
// // If we do not push a new job the miner will stay idle
|
||||
// // till a new job arrives
|
||||
//}
|
||||
}
|
||||
|
||||
float Miner::RetrieveHashRate() noexcept
|
||||
{
|
||||
return m_hashRate.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Miner::TriggerHashRateUpdate() noexcept
|
||||
{
|
||||
bool b = false;
|
||||
if (m_hashRateUpdate.compare_exchange_weak(b, true, std::memory_order_relaxed))
|
||||
return;
|
||||
// GPU didn't respond to last trigger, assume it's dead.
|
||||
// This can happen on CUDA if:
|
||||
// runtime of --cuda-grid-size * --cuda-streams exceeds time of m_collectInterval
|
||||
m_hashRate = 0.0;
|
||||
}
|
||||
|
||||
bool Miner::initEpoch()
|
||||
{
|
||||
// When loading of DAG is sequential wait for
|
||||
// this instance to become current
|
||||
if (s_dagLoadMode == DAG_LOAD_MODE_SEQUENTIAL)
|
||||
{
|
||||
while (s_dagLoadIndex < m_index)
|
||||
{
|
||||
boost::system_time const timeout =
|
||||
boost::get_system_time() + boost::posix_time::seconds(3);
|
||||
boost::mutex::scoped_lock l(x_work);
|
||||
m_dag_loaded_signal.timed_wait(l, timeout);
|
||||
}
|
||||
if (shouldStop())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run the internal initialization
|
||||
// specific for miner
|
||||
bool result = initEpoch_internal();
|
||||
|
||||
// Advance to next miner or reset to zero for
|
||||
// next run if all have processed
|
||||
if (s_dagLoadMode == DAG_LOAD_MODE_SEQUENTIAL)
|
||||
{
|
||||
s_dagLoadIndex = (m_index + 1);
|
||||
if (s_minersCount == s_dagLoadIndex)
|
||||
s_dagLoadIndex = 0;
|
||||
else
|
||||
m_dag_loaded_signal.notify_all();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WorkPackage Miner::work() const
|
||||
{
|
||||
boost::mutex::scoped_lock l(x_work);
|
||||
return m_work;
|
||||
}
|
||||
|
||||
void Miner::updateHashRate(uint32_t _groupSize, uint32_t _increment) noexcept
|
||||
{
|
||||
m_groupCount += _increment;
|
||||
bool b = true;
|
||||
if (!m_hashRateUpdate.compare_exchange_weak(b, false, std::memory_order_relaxed))
|
||||
return;
|
||||
using namespace std::chrono;
|
||||
auto t = steady_clock::now();
|
||||
auto us = duration_cast<microseconds>(t - m_hashTime).count();
|
||||
m_hashTime = t;
|
||||
|
||||
m_hashRate.store(
|
||||
us ? (float(m_groupCount * _groupSize) * 1.0e6f) / us : 0.0f, std::memory_order_relaxed);
|
||||
m_groupCount = 0;
|
||||
}
|
||||
|
||||
bool Miner::dropThreadPriority()
|
||||
{
|
||||
#if defined(__linux__)
|
||||
// Non Posix hack to lower compile thread's priority. Under POSIX
|
||||
// the nice value is a process attribute, under Linux it's a thread
|
||||
// attribute
|
||||
return nice(5) != -1;
|
||||
#elif defined(WIN32)
|
||||
return SetThreadPriority(m_compileThread->native_handle(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // namespace eth
|
||||
} // namespace dev
|
||||
485
zano/libethcore/Miner.h
Normal file
485
zano/libethcore/Miner.h
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
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
|
||||
Reference in New Issue
Block a user