Add xenoncat support (AVX1 and AVX2)

This commit is contained in:
Vlatko Kosturjak 2016-10-30 00:10:37 +02:00
parent 9afe293806
commit d2016f59d7
4 changed files with 271 additions and 8 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "nheqminer/external/equihash-xenon"]
path = nheqminer/external/equihash-xenon
url = https://github.com/xenoncat/equihash-xenon

View File

@ -2,19 +2,42 @@
cmake_minimum_required(VERSION 2.8)
option(STATIC_BUILD "Build with static libraries on Linux")
option(XENON "Build options with xenoncat")
option(MARCH "GCC options for architecture (default: native)")
project(nheqminer)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# LINUX
if(CMAKE_COMPILER_IS_GNUCXX)
if (MARCH)
set (GCCMARCH MARCH)
else()
set (GCCMARCH "-march=native")
endif()
# use native cpu features
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCCMARCH}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCCMARCH}")
# optimizations
add_definitions(-O3)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if (XENON EQUAL 0)
unset (XENONCAT)
elseif (XENON EQUAL 1)
set (XENONCAT "${nheqminer_SOURCE_DIR}/external/equihash-xenon/Linux/asm/equihash_avx1.o")
add_definitions(-DXENONCAT=1)
elseif (XENON EQUAL 2)
set (XENONCAT "${nheqminer_SOURCE_DIR}/external/equihash-xenon/Linux/asm/equihash_avx2.o")
add_definitions(-DXENONCAT=2)
else()
set (XENONCAT "${nheqminer_SOURCE_DIR}/external/equihash-xenon/Linux/asm/equihash_avx2.o")
add_definitions(-DXENONCAT=2)
endif()
endif()
# Common
include_directories(${nheqminer_SOURCE_DIR})
@ -24,8 +47,6 @@ else()
add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_ALL_DYN_LINK -DBOOST_LOG_DYN_LINK)
endif()
find_package(Threads REQUIRED COMPONENTS)
find_package(Boost REQUIRED COMPONENTS system log_setup log date_time filesystem thread)
@ -136,12 +157,15 @@ endif()
set(LIBS ${LIBS} ${Threads_LIBRARIES} ${Boost_LIBRARIES})
message("-- CXXFLAGS: ${CMAKE_CXX_FLAGS}")
message("-- LIBS: ${LIBS}")
message("-- XENON: ${XENON}")
message("-- XENONCAT: ${XENONCAT}")
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
#target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
target_link_libraries(${PROJECT_NAME} ${LIBS})
# target_link_libraries(${PROJECT_NAME} ${LIBS} ${CMAKE_THREAD_LIBS_INIT} ${nheqminer_SOURCE_DIR}/external/equihash-xenon/Linux/asm/equihash_avx2.o)
target_link_libraries(${PROJECT_NAME} ${LIBS} ${CMAKE_THREAD_LIBS_INIT} ${XENONCAT})
if (STATIC_BUILD)
# set_target_properties(${PROJECT_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_SEARCH_END_STATIC 1)

1
nheqminer/external/equihash-xenon vendored Submodule

@ -0,0 +1 @@
Subproject commit ebd7d3d89693a9627f3064851e32f7fca086c259

View File

@ -30,6 +30,11 @@ typedef uint32_t eh_index;
#define BOOST_LOG_CUSTOM(sev, pos) BOOST_LOG_TRIVIAL(sev) << "miner#" << pos << " | "
#ifdef XENONCAT
#define CONTEXT_SIZE 178033152
extern "C" void EhPrepare(void *context, void *input);
extern "C" int32_t EhSolver(void *context, uint32_t nonce);
#endif
void CompressArray(const unsigned char* in, size_t in_len,
unsigned char* out, size_t out_len,
@ -96,8 +101,212 @@ std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
return ret;
}
#ifdef XENONCAT
void static XenoncatZcashMinerThread(ZcashMiner* miner, int size, int pos)
{
BOOST_LOG_CUSTOM(info, pos) << "Starting thread #" << pos;
void static ZcashMinerThread(ZcashMiner* miner, int size, int pos)
unsigned int n = PARAMETER_N;
unsigned int k = PARAMETER_K;
std::shared_ptr<std::mutex> m_zmt(new std::mutex);
CBlockHeader header;
arith_uint256 space;
size_t offset;
arith_uint256 inc;
arith_uint256 target;
std::string jobId;
std::string nTime;
std::atomic_bool workReady {false};
std::atomic_bool cancelSolver {false};
std::atomic_bool pauseMining {false};
miner->NewJob.connect(NewJob_t::slot_type(
[&m_zmt, &header, &space, &offset, &inc, &target, &workReady, &cancelSolver, pos, &pauseMining, &jobId, &nTime]
(const ZcashJob* job) mutable {
std::lock_guard<std::mutex> lock{*m_zmt.get()};
if (job) {
BOOST_LOG_CUSTOM(debug, pos) << "Loading new job #" << job->jobId();
jobId = job->jobId();
nTime = job->time;
header = job->header;
space = job->nonce2Space;
offset = job->nonce1Size * 4; // Hex length to bit length
inc = job->nonce2Inc;
target = job->serverTarget;
pauseMining.store(false);
workReady.store(true);
/*if (job->clean) {
cancelSolver.store(true);
}*/
} else {
workReady.store(false);
cancelSolver.store(true);
pauseMining.store(true);
}
}
).track_foreign(m_zmt)); // So the signal disconnects when the mining thread exits
// Initialize context memory.
void* context_alloc = malloc(CONTEXT_SIZE+4096);
void* context = (void*) (((long) context_alloc+4095) & -4096);
try {
while (true) {
// Wait for work
bool expected;
do {
expected = true;
if (!miner->minerThreadActive[pos])
throw boost::thread_interrupted();
//boost::this_thread::interruption_point();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
} while (!workReady.compare_exchange_weak(expected, false));
// TODO change atomically with workReady
cancelSolver.store(false);
// Calculate nonce limits
arith_uint256 nonce;
arith_uint256 nonceEnd;
CBlockHeader actualHeader;
std::string actualJobId;
std::string actualTime;
size_t actualNonce1size;
{
std::lock_guard<std::mutex> lock{*m_zmt.get()};
arith_uint256 baseNonce = UintToArith256(header.nNonce);
arith_uint256 add(pos);
nonce = baseNonce | (add << (8 * 31));
nonceEnd = baseNonce | ((add + 1) << (8 * 31));
//nonce = baseNonce + ((space/size)*pos << offset);
//nonceEnd = baseNonce + ((space/size)*(pos+1) << offset);
// save job id and time
actualHeader = header;
actualJobId = jobId;
actualTime = nTime;
actualNonce1size = offset / 4;
}
// I = the block header minus nonce and solution.
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
{
//std::lock_guard<std::mutex> lock{ *m_zmt.get() };
CEquihashInput I{ actualHeader };
ss << I;
}
const char *tequihash_header = (char *)&ss[0];
unsigned int tequihash_header_len = ss.size();
// Start working
while (true) {
BOOST_LOG_CUSTOM(debug, pos) << "Running Equihash solver with nNonce = " << nonce.ToString();
auto bNonce = ArithToUint256(nonce);
std::function<bool(std::vector<unsigned char>)> validBlock =
[&m_zmt, &actualHeader, &bNonce, &target, &miner, pos, &actualJobId, &actualTime, &actualNonce1size]
(std::vector<unsigned char> soln) {
//std::lock_guard<std::mutex> lock{*m_zmt.get()};
// Write the solution to the hash and compute the result.
BOOST_LOG_CUSTOM(debug, pos) << "Checking solution against target...";
actualHeader.nNonce = bNonce;
actualHeader.nSolution = soln;
speed.AddSolution();
uint256 headerhash = actualHeader.GetHash();
if (UintToArith256(headerhash) > target) {
BOOST_LOG_CUSTOM(debug, pos) << "Too large: " << headerhash.ToString();
return false;
}
// Found a solution
BOOST_LOG_CUSTOM(debug, pos) << "Found solution with header hash: " << headerhash.ToString();
EquihashSolution solution{ bNonce, soln, actualTime, actualNonce1size };
miner->submitSolution(solution, actualJobId);
// We're a pooled miner, so try all solutions
return false;
};
//////////////////////////////////////////////////////////////////////////
// Xenoncat solver.
/////////////////////////////////////////////////////////////////////////
// bnonce is 32 bytes, read last four bytes as nonce int and send it to
// eh solver method.
unsigned char *tequihash_header = (unsigned char *)&ss[0];
unsigned int tequihash_header_len = ss.size();
unsigned char inputheader[144];
memcpy(inputheader, tequihash_header, tequihash_header_len);
// Write 32 byte nonce to input header.
uint256 arthNonce = ArithToUint256(nonce);
memcpy(inputheader + tequihash_header_len, (unsigned char*) arthNonce.begin(), arthNonce.size());
EhPrepare(context, (void *) inputheader);
unsigned char* nonceBegin = bNonce.begin();
uint32_t nonceToApi = *(uint32_t *)(nonceBegin+28);
uint32_t numsolutions = EhSolver(context, nonceToApi);
if (!cancelSolver.load()) {
for (uint32_t i=0; i<numsolutions; i++) {
// valid block method expects vector of unsigned chars.
unsigned char* solutionStart = (unsigned char*)(((unsigned char*)context)+1344*i);
unsigned char* solutionEnd = solutionStart + 1344;
std::vector<unsigned char> solution(solutionStart, solutionEnd);
validBlock(solution);
}
}
speed.AddHash(); // Metrics, add one hash execution.
//////////////////////////////////////////////////////////////////////////
// Xenoncat solver.
/////////////////////////////////////////////////////////////////////////
// Check for stop
if (!miner->minerThreadActive[pos])
throw boost::thread_interrupted();
//boost::this_thread::interruption_point();
// Update nonce
nonce += inc;
if (nonce == nonceEnd) {
break;
}
// Check for new work
if (workReady.load()) {
BOOST_LOG_CUSTOM(debug, pos) << "New work received, dropping current work";
break;
}
if (pauseMining.load())
{
BOOST_LOG_CUSTOM(debug, pos) << "Mining paused";
break;
}
}
}
}
catch (const boost::thread_interrupted&)
{
BOOST_LOG_CUSTOM(info, pos) << "Thread #" << pos << " terminated";
//throw;
return;
}
catch (const std::runtime_error &e)
{
BOOST_LOG_CUSTOM(info, pos) << "Runtime error: " << e.what();
return;
}
// Free the memory allocated previously for xenoncat context.
free(context_alloc);
}
#endif
void static TrompZcashMinerThread(ZcashMiner* miner, int size, int pos)
{
BOOST_LOG_CUSTOM(info, pos) << "Starting thread #" << pos;
@ -308,6 +517,32 @@ void static ZcashMinerThread(ZcashMiner* miner, int size, int pos)
}
}
void static ZcashMinerThread(ZcashMiner* miner, int size, int pos)
{
#ifdef XENONCAT
#if XENONCAT == 2
if (__builtin_cpu_supports("avx2")) {
BOOST_LOG_CUSTOM(info, pos) << "Using Xenoncat's AVX2 solver. ";
XenoncatZcashMinerThread(miner, size, pos);
}
#elif XENONCAT == 1
if (__builtin_cpu_supports("avx")) {
BOOST_LOG_CUSTOM(info, pos) << "Using Xenoncat's AVX solver. ";
XenoncatZcashMinerThread(miner, size, pos);
}
#else
if (0) {}
#endif
else {
#endif
BOOST_LOG_CUSTOM(info, pos) << "Using Tromp's solver.";
TrompZcashMinerThread(miner, size, pos);
#ifdef XENONCAT
}
#endif
}
ZcashJob* ZcashJob::clone() const
{
ZcashJob* ret = new ZcashJob();
@ -652,4 +887,4 @@ void do_benchmark(int nThreads, int hashes)
std::cout << "Total solutions found: " << benchmark_solutions << std::endl;
std::cout << "Speed: " << ((double)hashes_done * 1000 / (double)msec) << " H/s" << std::endl;
std::cout << "Speed: " << ((double)benchmark_solutions * 1000 / (double)msec) << " S/s" << std::endl;
}
}