diff --git a/libethash-cu/ethash_cu_miner.cpp b/libethash-cu/ethash_cu_miner.cpp index 4d2f3d2be..a4aec1829 100644 --- a/libethash-cu/ethash_cu_miner.cpp +++ b/libethash-cu/ethash_cu_miner.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "ethash_cu_miner.h" #include "ethash_cu_miner_kernel_globals.h" diff --git a/libethash-cu/ethash_cu_miner_kernel.h b/libethash-cu/ethash_cu_miner_kernel.h index 6f22ed447..4f46b7d44 100644 --- a/libethash-cu/ethash_cu_miner_kernel.h +++ b/libethash-cu/ethash_cu_miner_kernel.h @@ -2,7 +2,7 @@ #define _ETHASH_CU_MINER_KERNEL_H_ #include - +#include typedef union { @@ -32,7 +32,6 @@ typedef union uint4 uint4s[128 / sizeof(uint4)]; } hash128_t; -//typedef uint32_t hash128_t; cudaError set_constants( uint32_t * dag_size, diff --git a/libethcore/EthashCUDAMiner.cpp b/libethcore/EthashCUDAMiner.cpp new file mode 100644 index 000000000..36ef206bb --- /dev/null +++ b/libethcore/EthashCUDAMiner.cpp @@ -0,0 +1,238 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-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 cpp-ethereum. If not, see . +*/ +/** @file EthashGPUMiner.cpp + * @author Gav Wood + * @date 2014 + * + * Determines the PoW algorithm. + */ + +#if ETH_ETHASHCU || !ETH_TRUE + +#include "EthashCUDAMiner.h" +#include +#include +#include +using namespace std; +using namespace dev; +using namespace eth; + +namespace dev +{ +namespace eth +{ + +class EthashCUHook: public ethash_cu_miner::search_hook +{ +public: + EthashCUHook(EthashCUDAMiner* _owner): m_owner(_owner) {} + EthashCUHook(EthashCUHook const&) = delete; + + void abort() + { + { + UniqueGuard l(x_all); + if (m_aborted) + return; +// cdebug << "Attempting to abort"; + + m_abort = true; + } + // m_abort is true so now searched()/found() will return true to abort the search. + // we hang around on this thread waiting for them to point out that they have aborted since + // otherwise we may end up deleting this object prior to searched()/found() being called. + m_aborted.wait(true); +// for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) +// std::this_thread::sleep_for(chrono::milliseconds(30)); +// if (!m_aborted) +// cwarn << "Couldn't abort. Abandoning OpenCL process."; + } + + void reset() + { + UniqueGuard l(x_all); + m_aborted = m_abort = false; + } + +protected: + virtual bool found(uint64_t const* _nonces, uint32_t _count) override + { +// dev::operator <<(std::cerr << "Found nonces: ", vector(_nonces, _nonces + _count)) << std::endl; + for (uint32_t i = 0; i < _count; ++i) + if (m_owner->report(_nonces[i])) + return (m_aborted = true); + return m_owner->shouldStop(); + } + + virtual bool searched(uint64_t _startNonce, uint32_t _count) override + { + UniqueGuard l(x_all); +// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl; + m_owner->accumulateHashes(_count); + m_last = _startNonce + _count; + if (m_abort || m_owner->shouldStop()) + return (m_aborted = true); + return false; + } + +private: + Mutex x_all; + uint64_t m_last; + bool m_abort = false; + Notified m_aborted = {true}; + EthashCUDAMiner* m_owner = nullptr; +}; + +} +} + +unsigned EthashCUDAMiner::s_platformId = 0; +unsigned EthashCUDAMiner::s_deviceId = 0; +unsigned EthashCUDAMiner::s_numInstances = 0; + +EthashCUDAMiner::EthashCUDAMiner(ConstructionInfo const& _ci) : + GenericMiner(_ci), + Worker("cudaminer" + toString(index())), + m_hook(new EthashCUDAMiner(this)) +{ +} + +EthashCUDAMiner::~EthashCUDAMiner() +{ + pause(); + delete m_miner; + delete m_hook; +} + +bool EthashCUDAMiner::report(uint64_t _nonce) +{ + Nonce n = (Nonce)(u64)_nonce; + EthashProofOfWork::Result r = EthashAux::eval(work().seedHash, work().headerHash, n); + if (r.value < work().boundary) + return submitProof(Solution{n, r.mixHash}); + return false; +} + +void EthashCUDAMiner::kickOff() +{ + m_hook->reset(); + startWorking(); +} + +void EthashCUDAMiner::workLoop() +{ + // take local copy of work since it may end up being overwritten by kickOff/pause. + try { + WorkPackage w = work(); + cnote << "workLoop" << !!m_miner << m_minerSeed << w.seedHash; + if (!m_miner || m_minerSeed != w.seedHash) + { + cnote << "Initialising miner..."; + m_minerSeed = w.seedHash; + + delete m_miner; + m_miner = new ethash_cu_miner; + + unsigned device = instances() > 1 ? index() : s_deviceId; + + EthashAux::FullType dag; + while (true) + { + if ((dag = EthashAux::full(w.seedHash, true))) + break; + if (shouldStop()) + { + delete m_miner; + m_miner = nullptr; + return; + } + cnote << "Awaiting DAG"; + this_thread::sleep_for(chrono::milliseconds(500)); + } + bytesConstRef dagData = dag->data(); + m_miner->init(dagData.data(), dagData.size(), s_platformId, device); + } + + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); + m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook); + } + catch (cl::Error const& _e) + { + delete m_miner; + m_miner = nullptr; + cwarn << "Error GPU mining: " << _e.what() << "(" << _e.err() << ")"; + } +} + +void EthashCUDAMiner::pause() +{ + m_hook->abort(); + stopWorking(); +} + +std::string EthashCUDAMiner::platformInfo() +{ + return ethash_cl_miner::platform_info(s_platformId, s_deviceId); +} + +unsigned EthashCUDAMiner::getNumDevices() +{ + return ethash_cl_miner::getNumDevices(s_platformId); +} + +void EthashCUDAMiner::listDevices() +{ + return ethash_cl_miner::listDevices(); +} + +bool EthashCUDAMiner::configureGPU( + unsigned _localWorkSize, + unsigned _globalWorkSizeMultiplier, + unsigned _msPerBatch, + unsigned _platformId, + unsigned _deviceId, + bool _allowCPU, + unsigned _extraGPUMemory, + uint64_t _currentBlock +) +{ + s_platformId = _platformId; + s_deviceId = _deviceId; + + if (_localWorkSize != 32 && _localWorkSize != 64 && _localWorkSize != 128 && _localWorkSize != 256) + { + cout << "Given localWorkSize of " << toString(_localWorkSize) << "is invalid. Must be either 32,64,128 or 256" << endl; + return false; + } + + if (!ethash_cu_miner::configureGPU( + _platformId, + _localWorkSize, + _globalWorkSizeMultiplier * _localWorkSize, + _msPerBatch, + _allowCPU, + _extraGPUMemory, + _currentBlock) + ) + { + cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl; + return false; + } + return true; +} + +#endif diff --git a/libethcore/EthashCUDAMiner.h b/libethcore/EthashCUDAMiner.h new file mode 100644 index 000000000..080c6ddcf --- /dev/null +++ b/libethcore/EthashCUDAMiner.h @@ -0,0 +1,82 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-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 cpp-ethereum. If not, see . +*/ +/** @file EthashGPUMiner.h + * @author Gav Wood + * @date 2014 + * + * Determines the PoW algorithm. + */ + +#pragma once +#if ETH_ETHASHCU || !ETH_TRUE + +#include "libdevcore/Worker.h" +#include "EthashAux.h" +#include "Miner.h" + +namespace dev +{ +namespace eth +{ + +class EthashCUDAMiner: public GenericMiner, Worker +{ + friend class dev::eth::EthashCLHook; + +public: + EthashCUDAMiner(ConstructionInfo const& _ci); + ~EthashCUDAMiner(); + + static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; } + static std::string platformInfo(); + static unsigned getNumDevices(); + static void listDevices(); + static bool configureGPU( + unsigned _localWorkSize, + unsigned _globalWorkSizeMultiplier, + unsigned _msPerBatch, + unsigned _platformId, + unsigned _deviceId, + bool _allowCPU, + unsigned _extraGPUMemory, + uint64_t _currentBlock + ); + static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, getNumDevices()); } + +protected: + void kickOff() override; + void pause() override; + +private: + void workLoop() override; + bool report(uint64_t _nonce); + + using GenericMiner::accumulateHashes; + + EthashCLHook* m_hook = nullptr; + ethash_cl_miner* m_miner = nullptr; + + h256 m_minerSeed; ///< Last seed in m_miner + static unsigned s_platformId; + static unsigned s_deviceId; + static unsigned s_numInstances; +}; + +} +} + +#endif