Browse Source

GPU Miner prototyped in new API.

cl-refactor
Gav Wood 10 years ago
parent
commit
87a160ab21
  1. 132
      libethcore/Ethasher.cpp
  2. 10
      libethcore/Ethasher.h
  3. 86
      libethcore/ProofOfWork.cpp
  4. 32
      libethcore/ProofOfWork.h

132
libethcore/Ethasher.cpp

@ -39,6 +39,8 @@ using namespace chrono;
using namespace dev;
using namespace eth;
#define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
Ethasher* dev::eth::Ethasher::s_this = nullptr;
Ethasher::~Ethasher()
@ -59,28 +61,35 @@ void Ethasher::killCache(h256 const& _s)
void const* Ethasher::light(BlockInfo const& _header)
{
RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048)
{
std::ostringstream error;
error << "block number is too high; max is " << c_ethashEpochLength * 2048 << "(was " << _header.number << ")";
throw std::invalid_argument( error.str() );
}
return light(_header.seedHash());
}
void const* Ethasher::light(h256 const& _seedHash)
{
RecursiveGuard l(x_this);
if (!m_lights.count(_header.seedHash()))
{
ethash_params p = params((unsigned)_header.number);
m_lights[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
ethash_params p = params(_seedHash);
m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data());
}
return m_lights[_header.seedHash()];
return m_lights[_seedHash];
}
#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
bytesConstRef Ethasher::full(BlockInfo const& _header, bytesRef _dest)
{
return full(_header.seedHash(), _dest);
}
bytesConstRef Ethasher::full(BlockInfo const& _header)
bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest)
{
RecursiveGuard l(x_this);
if (!m_fulls.count(_header.seedHash()))
if (m_fulls.count(_seedHash) && _dest)
{
assert(m_fulls.size() <= _dest.size());
m_fulls.at(_seedHash).copyTo(_dest);
return;
}
if (!m_fulls.count(_seedHash))
{
// @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr.
/* if (!m_fulls.empty())
@ -93,9 +102,9 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
auto info = rlpList(c_ethashRevision, _header.seedHash());
auto info = rlpList(c_ethashRevision, _seedHash);
std::string oldMemoFile = getDataDir("ethash") + "/full";
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8));
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_seedHash.ref().cropped(0, 8));
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
{
// memofile valid - rename.
@ -105,17 +114,26 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile));
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info"));
m_fulls[_header.seedHash()] = contentsNew(memoFile);
if (!m_fulls[_header.seedHash()])
ethash_params p = params(_seedHash);
assert(!_dest || _dest.size() >= p.full_size); // must be big enough.
bytesRef r = contentsNew(memoFile, _dest);
if (!r)
{
ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = light(_header);
ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c);
writeFile(memoFile, m_fulls[_header.seedHash()]);
// file didn't exist.
if (_dest)
// buffer was passed in - no insertion into cache nor need to allocate
r = _dest;
else
r = bytesRef(new byte[p.full_size], p.full_size);
ethash_prep_full(r, &p, light(_seedHash));
writeFile(memoFile, r);
}
if (_dest)
return _dest;
m_fulls[_seedHash] = r;
}
return m_fulls[_header.seedHash()];
return m_fulls[_seedHash];
}
ethash_params Ethasher::params(BlockInfo const& _header)
@ -123,50 +141,33 @@ ethash_params Ethasher::params(BlockInfo const& _header)
return params((unsigned)_header.number);
}
void Ethasher::readFull(BlockInfo const& _header, void* _dest)
ethash_params Ethasher::params(unsigned _n)
{
if (!m_fulls.count(_header.seedHash()))
{
// @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr.
/* if (!m_fulls.empty())
{
delete [] m_fulls.begin()->second.data();
m_fulls.erase(m_fulls.begin());
}*/
try {
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
ethash_params p;
p.cache_size = ethash_get_cachesize(_n);
p.full_size = ethash_get_datasize(_n);
return p;
}
auto info = rlpList(c_ethashRevision, _header.seedHash());
std::string oldMemoFile = getDataDir("ethash") + "/full";
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8));
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
ethash_params Ethasher::params(h256 const& _seedHash)
{
unsigned epoch = 0;
try
{
// memofile valid - rename.
boost::filesystem::rename(oldMemoFile, memoFile);
epoch = m_seedHashes.at(_seedHash);
}
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile));
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info"));
ethash_params p = params((unsigned)_header.number);
bytesRef r = contentsNew(memoFile, bytesRef((byte*)_dest, p.full_size));
if (!r)
catch (...)
{
auto c = light(_header);
ethash_prep_full(_dest, &p, c);
writeFile(memoFile, bytesConstRef((byte*)_dest, p.full_size));
for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {}
if (epoch == 2048)
{
std::ostringstream error;
error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (c_ethashEpochLength * 2048);
throw std::invalid_argument(error.str());
}
m_seedHashes[_seedHash] = epoch;
}
}
ethash_params Ethasher::params(unsigned _n)
{
ethash_params p;
p.cache_size = ethash_get_cachesize(_n);
p.full_size = ethash_get_datasize(_n);
return p;
return params(epoch * c_ethashEpochLength);
}
bool Ethasher::verify(BlockInfo const& _header)
@ -208,13 +209,18 @@ bool Ethasher::verify(BlockInfo const& _header)
}
Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{
return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce);
}
Ethasher::Result Ethasher::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{
auto p = Ethasher::params(_header);
ethash_return_value r;
if (Ethasher::get()->m_fulls.count(_header.seedHash()))
ethash_compute_full(&r, Ethasher::get()->full(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
if (Ethasher::get()->m_fulls.count(_seedHash))
ethash_compute_full(&r, Ethasher::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce);
else
ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
ethash_compute_light(&r, Ethasher::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce);
// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
}

10
libethcore/Ethasher.h

@ -52,12 +52,14 @@ public:
using FullType = void const*;
LightType light(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header);
LightType light(h256 const& _header);
bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef());
bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef());
static ethash_params params(BlockInfo const& _header);
static ethash_params params(h256 const& _seedHash);
static ethash_params params(unsigned _n);
void readFull(BlockInfo const& _header, void* _dest);
struct Result
{
h256 value;
@ -66,6 +68,7 @@ public:
static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Result eval(BlockInfo const& _header, Nonce const& _nonce);
static Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce);
static bool verify(BlockInfo const& _header);
class Miner
@ -103,6 +106,7 @@ private:
RecursiveMutex x_this;
std::map<h256, LightType> m_lights;
std::map<h256, bytesRef> m_fulls;
std::map<h256, unsigned> m_seedHashes;
};
}

86
libethcore/ProofOfWork.cpp

@ -31,7 +31,7 @@
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/FileSystem.h>
#include <libdevcore/Common.h>
#if ETH_ETHASHCL
#if ETH_ETHASHCL || !ETH_TRUE
#include <libethash-cl/ethash_cl_miner.h>
#endif
#include "BlockInfo.h"
@ -134,10 +134,15 @@ public:
};
*/
struct EthashCLHook: public ethash_cl_miner::search_hook
namespace dev { namespace eth {
class EthashCLHook: public ethash_cl_miner::search_hook
{
public:
EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {}
void abort()
{
Guard l(x_all);
if (m_aborted)
return;
// cdebug << "Attempting to abort";
@ -147,22 +152,24 @@ struct EthashCLHook: public ethash_cl_miner::search_hook
if (!m_aborted)
cwarn << "Couldn't abort. Abandoning OpenCL process.";
m_aborted = m_abort = false;
m_found.clear();
}
vector<Nonce> fetchFound() { vector<Nonce> ret; Guard l(x_all); std::swap(ret, m_found); return ret; }
uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; }
protected:
virtual bool found(uint64_t const* _nonces, uint32_t _count) override
{
Guard l(x_all);
for (unsigned i = 0; i < _count; ++i)
m_found.push_back((Nonce)(u64)_nonces[i]);
m_aborted = true;
// cdebug << "Found nonces: " << vector<uint64_t>(_nonces, _nonces + _count);
for (uint32_t i = 0; i < _count; ++i)
{
if (m_owner->found(_nonces[i]))
{
m_aborted = true;
return true;
}
}
return false;
}
virtual bool searched(uint64_t _startNonce, uint32_t _count) override
{
@ -180,66 +187,53 @@ protected:
private:
Mutex x_all;
vector<Nonce> m_found;
uint64_t m_total;
uint64_t m_last;
bool m_abort = false;
bool m_aborted = true;
Ethash::GPUMiner* m_owner = nullptr;
};
EthashCL::EthashCL():
m_hook(new EthashCLHook)
} }
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci),
m_hook(new EthashCLHook(this))
{
}
EthashCL::~EthashCL()
void Ethash::GPUMiner::report(uint64_t _nonce)
{
Nonce n = (Nonce)(u64)_nonce;
Ethasher::Result r = Ethasher::eval(m_work.seedHash, m_work.headerHash, n);
if (r.value < m_work.boundary)
return submitProof(Solution{n, r.mixHash});
return false;
}
std::pair<MineInfo, Ethash::Solution> EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool)
void Ethash::GPUMiner::kickOff(WorkPackage const& _work)
{
if (!m_lastHeader || m_lastHeader.seedHash() != _header.seedHash())
if (!m_miner || m_minerSeed != _work.seedHash)
{
if (m_miner)
m_hook->abort();
m_miner.reset(new ethash_cl_miner);
auto cb = [&](void* d) {
Ethasher::get()->readFull(_header, d);
};
m_miner->init(Ethasher::params(_header), cb, 32);
auto p = Ethasher::params(_work.seedHash);
auto cb = [&](void* d) { Ethasher::get()->readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); };
m_miner->init(p, cb, 32);
}
if (m_lastHeader != _header)
if (m_lastWork.headerHash != _work.headerHash)
{
m_hook->abort();
static std::random_device s_eng;
auto hh = _header.headerHash(WithoutNonce);
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_header.boundary() >> 192);
m_miner->search(hh.data(), upper64OfBoundary, *m_hook);
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192);
m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook);
}
m_lastHeader = _header;
MineInfo mi;
Solution proof;
mi.requirement = log2((double)(u256)_header.boundary());
mi.best = 0;
std::this_thread::sleep_for(chrono::milliseconds(_msTimeout));
m_work = _work;
}
mi.hashes += m_hook->fetchTotal();
auto found = m_hook->fetchFound();
if (!found.empty())
{
for (auto const& n: found)
{
auto result = Ethasher::eval(_header, n);
if (result.value < _header.boundary())
{
mi.completed = true;
proof = Solution{n, result.mixHash};
}
}
}
return std::make_pair(mi, proof);
void Ethash::GPUMiner::pause()
{
m_hook->abort();
}
#endif

32
libethcore/ProofOfWork.h

@ -34,7 +34,6 @@
#define FAKE_DAGGER 1
class ethash_cl_miner;
struct ethash_cl_search_hook;
namespace dev
{
@ -52,8 +51,6 @@ struct MineInfo
bool completed = false;
};
class EthashCLHook;
class Ethash
{
@ -75,6 +72,7 @@ public:
static unsigned instances() { return thread::hardware_concurrency(); }
protected:
void kickOff(WorkPackage const& _work) override
{
stopWorking();
@ -93,25 +91,29 @@ private:
#if ETH_ETHASHCL || !ETH_TRUE
class GPUMiner: public NewMiner
class EthashCLHook;
class GPUMiner: public Miner
{
public:
GPUMiner(ConstructionInfo const& _ci): NewMiner(_ci)
{
friend class EthashCLHook;
}
public:
GPUMiner(ConstructionInfo const& _ci);
static unsigned instances() { return 1; }
std::pair<MineInfo, Solution> mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override;
unsigned defaultTimeout() const override { return 500; }
protected:
Nonce m_last;
BlockInfo m_lastHeader;
Nonce m_mined;
std::unique_ptr<ethash_cl_miner> m_miner;
void kickOff(WorkPackage const& _work) override;
void pause() override;
private:
void report(uint64_t _nonce);
std::unique_ptr<EthashCLHook> m_hook;
std::unique_ptr<ethash_cl_miner> m_miner;
h256 m_minerSeed;
WorkPackage m_lastWork; ///< Work loaded into m_miner.
MineInfo m_info;
};
#else

Loading…
Cancel
Save