GhostRider algorithm (Raptoreum) support

This commit is contained in:
SChernykh 2021-11-13 20:12:15 +01:00
parent 5156ff11a8
commit ceaebfd877
67 changed files with 72022 additions and 177 deletions

View file

@ -0,0 +1,836 @@
/* XMRig
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ghostrider.h"
#include "sph_blake.h"
#include "sph_bmw.h"
#include "sph_groestl.h"
#include "sph_jh.h"
#include "sph_keccak.h"
#include "sph_skein.h"
#include "sph_luffa.h"
#include "sph_cubehash.h"
#include "sph_shavite.h"
#include "sph_simd.h"
#include "sph_echo.h"
#include "sph_hamsi.h"
#include "sph_fugue.h"
#include "sph_shabal.h"
#include "sph_whirlpool.h"
#include "base/io/log/Log.h"
#include "base/io/log/Tags.h"
#include "backend/cpu/Cpu.h"
#include "crypto/cn/CnHash.h"
#include "crypto/cn/CnCtx.h"
#include "crypto/cn/CryptoNight.h"
#include "crypto/common/VirtualMemory.h"
#include <thread>
#include <atomic>
#include <chrono>
#include <uv.h>
#ifdef XMRIG_FEATURE_HWLOC
#include "base/kernel/Platform.h"
#include "backend/cpu/platform/HwlocCpuInfo.h"
#include <hwloc.h>
#endif
#ifdef XMRIG_ARM
# include "crypto/cn/sse2neon.h"
#endif
#define CORE_HASH(i, x) static void h##i(const uint8_t* data, size_t size, uint8_t* output) \
{ \
sph_##x##_context ctx; \
sph_##x##_init(&ctx); \
sph_##x(&ctx, data, size); \
sph_##x##_close(&ctx, output); \
}
CORE_HASH( 0, blake512 );
CORE_HASH( 1, bmw512 );
CORE_HASH( 2, groestl512 );
CORE_HASH( 3, jh512 );
CORE_HASH( 4, keccak512 );
CORE_HASH( 5, skein512 );
CORE_HASH( 6, luffa512 );
CORE_HASH( 7, cubehash512);
CORE_HASH( 8, shavite512 );
CORE_HASH( 9, simd512 );
CORE_HASH(10, echo512 );
CORE_HASH(11, hamsi512 );
CORE_HASH(12, fugue512 );
CORE_HASH(13, shabal512 );
CORE_HASH(14, whirlpool );
#undef CORE_HASH
typedef void (*core_hash_func)(const uint8_t* data, size_t size, uint8_t* output);
static const core_hash_func core_hash[15] = { h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11, h12, h13, h14 };
namespace xmrig
{
static constexpr Algorithm::Id cn_hash[6] = {
Algorithm::CN_GR_0,
Algorithm::CN_GR_1,
Algorithm::CN_GR_2,
Algorithm::CN_GR_3,
Algorithm::CN_GR_4,
Algorithm::CN_GR_5,
};
static constexpr const char* cn_names[6] = {
"cn/dark (512 KB)",
"cn/dark-lite (256 KB)",
"cn/fast (2 MB)",
"cn/lite (1 MB)",
"cn/turtle (256 KB)",
"cn/turtle-lite (128 KB)",
};
static constexpr size_t cn_sizes[6] = {
Algorithm::l3(Algorithm::CN_GR_0), // 512 KB
Algorithm::l3(Algorithm::CN_GR_1) / 2, // 256 KB
Algorithm::l3(Algorithm::CN_GR_2), // 2 MB
Algorithm::l3(Algorithm::CN_GR_3), // 1 MB
Algorithm::l3(Algorithm::CN_GR_4), // 256 KB
Algorithm::l3(Algorithm::CN_GR_5) / 2, // 128 KB
};
static constexpr CnHash::AlgoVariant av_hw_aes[5] = { CnHash::AV_SINGLE, CnHash::AV_SINGLE, CnHash::AV_DOUBLE, CnHash::AV_TRIPLE, CnHash::AV_QUAD };
static constexpr CnHash::AlgoVariant av_soft_aes[5] = { CnHash::AV_SINGLE_SOFT, CnHash::AV_SINGLE_SOFT, CnHash::AV_DOUBLE_SOFT, CnHash::AV_TRIPLE_SOFT, CnHash::AV_QUAD_SOFT };
template<size_t N>
static inline void select_indices(uint32_t (&indices)[N], const uint8_t* seed)
{
bool selected[N] = {};
uint32_t k = 0;
for (uint32_t i = 0; i < 64; ++i) {
const uint8_t index = ((seed[i / 2] >> ((i & 1) * 4)) & 0xF) % N;
if (!selected[index]) {
selected[index] = true;
indices[k++] = index;
if (k >= N) {
return;
}
}
}
for (uint32_t i = 0; i < N; ++i) {
if (!selected[i]) {
indices[k++] = i;
}
}
}
namespace ghostrider
{
#ifdef XMRIG_FEATURE_HWLOC
static struct AlgoTune
{
double hashrate = 0.0;
uint32_t step = 1;
uint32_t threads = 1;
} tuneDefault[6], tune8MB[6];
struct HelperThread
{
HelperThread(hwloc_bitmap_t cpu_set, bool is8MB) : m_cpuSet(cpu_set), m_is8MB(is8MB)
{
uv_mutex_init(&m_mutex);
uv_cond_init(&m_cond);
m_thread = new std::thread(&HelperThread::run, this);
do {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} while (!m_ready);
}
~HelperThread()
{
uv_mutex_lock(&m_mutex);
m_finished = true;
uv_cond_signal(&m_cond);
uv_mutex_unlock(&m_mutex);
m_thread->join();
delete m_thread;
uv_mutex_destroy(&m_mutex);
uv_cond_destroy(&m_cond);
hwloc_bitmap_free(m_cpuSet);
}
struct TaskBase
{
virtual ~TaskBase() {}
virtual void run() = 0;
};
template<typename T>
struct Task : TaskBase
{
inline Task(T&& task) : m_task(std::move(task))
{
static_assert(sizeof(Task) <= 128, "Task struct is too large");
}
void run() override
{
m_task();
this->~Task();
}
T m_task;
};
template<typename T>
inline void launch_task(T&& task)
{
uv_mutex_lock(&m_mutex);
new (&m_tasks[m_numTasks++]) Task<T>(std::move(task));
uv_cond_signal(&m_cond);
uv_mutex_unlock(&m_mutex);
}
inline void wait() const
{
while (m_numTasks) {
_mm_pause();
}
}
void run()
{
if (hwloc_bitmap_weight(m_cpuSet) > 0) {
hwloc_topology_t topology = reinterpret_cast<HwlocCpuInfo*>(Cpu::info())->topology();
if (hwloc_set_cpubind(topology, m_cpuSet, HWLOC_CPUBIND_THREAD | HWLOC_CPUBIND_STRICT) < 0) {
hwloc_set_cpubind(topology, m_cpuSet, HWLOC_CPUBIND_THREAD);
}
}
uv_mutex_lock(&m_mutex);
m_ready = true;
do {
uv_cond_wait(&m_cond, &m_mutex);
const uint32_t n = m_numTasks;
if (n > 0) {
for (uint32_t i = 0; i < n; ++i) {
reinterpret_cast<TaskBase*>(&m_tasks[i])->run();
}
std::atomic_thread_fence(std::memory_order_seq_cst);
m_numTasks = 0;
}
} while (!m_finished);
uv_mutex_unlock(&m_mutex);
}
uv_mutex_t m_mutex;
uv_cond_t m_cond;
alignas(16) uint8_t m_tasks[4][128] = {};
volatile uint32_t m_numTasks = 0;
volatile bool m_ready = false;
volatile bool m_finished = false;
hwloc_bitmap_t m_cpuSet = {};
bool m_is8MB = false;
std::thread* m_thread = nullptr;
};
void benchmark()
{
static std::atomic<int> done{ 0 };
if (done.exchange(1)) {
return;
}
std::thread t([]() {
// Try to avoid CPU core 0 because many system threads use it and can interfere
uint32_t thread_index1 = (Cpu::info()->threads() > 2) ? 2 : 0;
hwloc_topology_t topology = reinterpret_cast<HwlocCpuInfo*>(Cpu::info())->topology();
hwloc_obj_t pu = hwloc_get_pu_obj_by_os_index(topology, thread_index1);
hwloc_obj_t pu2;
hwloc_get_closest_objs(topology, pu, &pu2, 1);
uint32_t thread_index2 = pu2->os_index;
if (thread_index2 < thread_index1) {
std::swap(thread_index1, thread_index2);
}
Platform::setThreadAffinity(thread_index1);
constexpr uint32_t N = 1U << 21;
VirtualMemory::init(0, N);
VirtualMemory* memory = new VirtualMemory(N * 8, true, false, false);
// 2 MB cache per core by default
size_t max_scratchpad_size = 1U << 21;
if ((Cpu::info()->L3() >> 22) > Cpu::info()->cores()) {
// At least 1 core can run with 8 MB cache
max_scratchpad_size = 1U << 23;
}
else if ((Cpu::info()->L3() >> 22) >= Cpu::info()->cores()) {
// All cores can run with 4 MB cache
max_scratchpad_size = 1U << 22;
}
LOG_VERBOSE("Running GhostRider benchmark on logical CPUs %u and %u (max scratchpad size %zu MB, huge pages %s)", thread_index1, thread_index2, max_scratchpad_size >> 20, memory->isHugePages() ? "on" : "off");
cryptonight_ctx* ctx[8];
CnCtx::create(ctx, memory->scratchpad(), N, 8);
const CnHash::AlgoVariant* av = Cpu::info()->hasAES() ? av_hw_aes : av_soft_aes;
uint8_t buf[80];
uint8_t hash[32 * 8];
LOG_VERBOSE("%24s | N | Hashrate", "Algorithm");
LOG_VERBOSE("-------------------------|-----|-------------");
using namespace std::chrono;
for (uint32_t algo = 0; algo < 6; ++algo) {
for (uint64_t step : { 1, 2, 4}) {
# ifdef XMRIG_ARM
if (step == 4) {
continue;
}
# endif
const size_t cur_scratchpad_size = cn_sizes[algo] * step;
if (cur_scratchpad_size > max_scratchpad_size) {
continue;
}
auto f = CnHash::fn(cn_hash[algo], av[step], Assembly::AUTO);
const high_resolution_clock::time_point start_time = high_resolution_clock::now();
double min_dt = 1e10;
for (uint32_t iter = 0;; ++iter) {
const high_resolution_clock::time_point t1 = high_resolution_clock::now();
// Stop after 15 milliseconds, but only if at least 10 iterations were done
if ((iter >= 10) && (duration_cast<milliseconds>(t1 - start_time).count() >= 15)) {
break;
}
f(buf, sizeof(buf), hash, ctx, 0);
const double dt = duration_cast<nanoseconds>(high_resolution_clock::now() - t1).count() / 1e9;
if (dt < min_dt) {
min_dt = dt;
}
}
const double hashrate = step / min_dt;
LOG_VERBOSE("%24s | %" PRIu64 "x1 | %.2f h/s", cn_names[algo], step, hashrate);
if (hashrate > tune8MB[algo].hashrate) {
tune8MB[algo].hashrate = hashrate;
tune8MB[algo].step = static_cast<uint32_t>(step);
tune8MB[algo].threads = 1;
}
if ((cur_scratchpad_size < (1U << 23)) && (hashrate > tuneDefault[algo].hashrate)) {
tuneDefault[algo].hashrate = hashrate;
tuneDefault[algo].step = static_cast<uint32_t>(step);
tuneDefault[algo].threads = 1;
}
}
}
hwloc_bitmap_t helper_set = hwloc_bitmap_alloc();
hwloc_bitmap_set(helper_set, thread_index2);
HelperThread* helper = new HelperThread(helper_set, false);
for (uint32_t algo = 0; algo < 6; ++algo) {
for (uint64_t step : { 1, 2, 4}) {
# ifdef XMRIG_ARM
if (step == 4) {
continue;
}
# endif
const size_t cur_scratchpad_size = cn_sizes[algo] * step * 2;
if (cur_scratchpad_size > max_scratchpad_size) {
continue;
}
auto f = CnHash::fn(cn_hash[algo], av[step], Assembly::AUTO);
const high_resolution_clock::time_point start_time = high_resolution_clock::now();
double min_dt = 1e10;
for (uint32_t iter = 0;; ++iter) {
const high_resolution_clock::time_point t1 = high_resolution_clock::now();
// Stop after 30 milliseconds, but only if at least 10 iterations were done
if ((iter >= 10) && (duration_cast<milliseconds>(t1 - start_time).count() >= 30)) {
break;
}
helper->launch_task([&f, &buf, &hash, &ctx, &step]() { f(buf, sizeof(buf), hash + step * 32, ctx + step, 0); });
f(buf, sizeof(buf), hash, ctx, 0);
helper->wait();
const double dt = duration_cast<nanoseconds>(high_resolution_clock::now() - t1).count() / 1e9;
if (dt < min_dt) {
min_dt = dt;
}
}
const double hashrate = step * 2.0 / min_dt * 1.0075;
LOG_VERBOSE("%24s | %" PRIu64 "x2 | %.2f h/s", cn_names[algo], step, hashrate);
if (hashrate > tune8MB[algo].hashrate) {
tune8MB[algo].hashrate = hashrate;
tune8MB[algo].step = static_cast<uint32_t>(step);
tune8MB[algo].threads = 2;
}
if ((cur_scratchpad_size < (1U << 23)) && (hashrate > tuneDefault[algo].hashrate)) {
tuneDefault[algo].hashrate = hashrate;
tuneDefault[algo].step = static_cast<uint32_t>(step);
tuneDefault[algo].threads = 2;
}
}
}
delete helper;
CnCtx::release(ctx, 8);
delete memory;
});
t.join();
LOG_VERBOSE("---------------------------------------------");
LOG_VERBOSE("| GhostRider tuning results |");
LOG_VERBOSE("---------------------------------------------");
for (int algo = 0; algo < 6; ++algo) {
LOG_VERBOSE("%24s | %ux%u | %.2f h/s", cn_names[algo], tuneDefault[algo].step, tuneDefault[algo].threads, tuneDefault[algo].hashrate);
if ((tune8MB[algo].step != tuneDefault[algo].step) || (tune8MB[algo].threads != tuneDefault[algo].threads)) {
LOG_VERBOSE("%24s | %ux%u | %.2f h/s", cn_names[algo], tune8MB[algo].step, tune8MB[algo].threads, tune8MB[algo].hashrate);
}
}
}
template <typename func>
static inline bool findByType(hwloc_obj_t obj, hwloc_obj_type_t type, func lambda)
{
for (size_t i = 0; i < obj->arity; i++) {
if (obj->children[i]->type == type) {
if (lambda(obj->children[i])) {
return true;
}
}
else {
if (findByType(obj->children[i], type, lambda)) {
return true;
}
}
}
return false;
}
HelperThread* create_helper_thread(int64_t cpu_index, const std::vector<int64_t>& affinities)
{
#ifndef XMRIG_ARM
hwloc_bitmap_t helper_cpu_set = hwloc_bitmap_alloc();
hwloc_bitmap_t main_threads_set = hwloc_bitmap_alloc();
for (int64_t i : affinities) {
if (i >= 0) {
hwloc_bitmap_set(main_threads_set, i);
}
}
if (cpu_index >= 0) {
hwloc_topology_t topology = reinterpret_cast<HwlocCpuInfo*>(Cpu::info())->topology();
hwloc_obj_t root = hwloc_get_root_obj(topology);
bool is8MB = false;
findByType(root, HWLOC_OBJ_L3CACHE, [cpu_index, &is8MB](hwloc_obj_t obj) {
if (!hwloc_bitmap_isset(obj->cpuset, cpu_index)) {
return false;
}
uint32_t num_cores = 0;
findByType(obj, HWLOC_OBJ_CORE, [&num_cores](hwloc_obj_t) { ++num_cores; return false; });
if ((obj->attr->cache.size >> 22) > num_cores) {
uint32_t num_8MB_cores = (obj->attr->cache.size >> 22) - num_cores;
is8MB = findByType(obj, HWLOC_OBJ_CORE, [cpu_index, &num_8MB_cores](hwloc_obj_t obj2) {
if (num_8MB_cores > 0) {
--num_8MB_cores;
if (hwloc_bitmap_isset(obj2->cpuset, cpu_index)) {
return true;
}
}
return false;
});
}
return true;
});
for (auto obj_type : { HWLOC_OBJ_CORE, HWLOC_OBJ_L1CACHE, HWLOC_OBJ_L2CACHE, HWLOC_OBJ_L3CACHE }) {
findByType(root, obj_type, [cpu_index, helper_cpu_set, main_threads_set](hwloc_obj_t obj) {
const hwloc_cpuset_t& s = obj->cpuset;
if (hwloc_bitmap_isset(s, cpu_index)) {
hwloc_bitmap_andnot(helper_cpu_set, s, main_threads_set);
if (hwloc_bitmap_weight(helper_cpu_set) > 0) {
return true;
}
}
return false;
});
if (hwloc_bitmap_weight(helper_cpu_set) > 0) {
return new HelperThread(helper_cpu_set, is8MB);
}
}
}
#endif
return nullptr;
}
void destroy_helper_thread(HelperThread* t)
{
delete t;
}
void hash_octa(const uint8_t* data, size_t size, uint8_t* output, cryptonight_ctx** ctx, HelperThread* helper)
{
enum { N = 8 };
uint8_t* ctx_memory[N];
for (size_t i = 0; i < N; ++i) {
ctx_memory[i] = ctx[i]->memory;
}
// PrevBlockHash (GhostRider's seed) is stored in bytes [4; 36)
uint32_t core_indices[15];
select_indices(core_indices, data + 4);
uint32_t cn_indices[6];
select_indices(cn_indices, data + 4);
static uint32_t prev_indices[3];
if (memcmp(cn_indices, prev_indices, sizeof(prev_indices)) != 0) {
memcpy(prev_indices, cn_indices, sizeof(prev_indices));
for (int i = 0; i < 3; ++i) {
LOG_INFO("%s GhostRider algo %d: %s", Tags::cpu(), i + 1, cn_names[cn_indices[i]]);
}
}
const CnHash::AlgoVariant* av = Cpu::info()->hasAES() ? av_hw_aes : av_soft_aes;
const AlgoTune* tune = (helper && helper->m_is8MB) ? tune8MB : tuneDefault;
uint8_t tmp[64 * N];
if (helper && (tune[cn_indices[0]].threads == 2) && (tune[cn_indices[1]].threads == 2) && (tune[cn_indices[2]].threads == 2)) {
const size_t n = N / 2;
helper->launch_task([n, av, data, size, &ctx_memory, ctx, &cn_indices, &core_indices, &tmp, output, tune]() {
const uint8_t* input = data;
size_t input_size = size;
for (size_t part = 0; part < 3; ++part) {
const AlgoTune& t = tune[cn_indices[part]];
// Allocate scratchpads
{
uint8_t* p = ctx_memory[4];
for (size_t i = n, k = 4; i < N; ++i) {
if ((i % t.step) == 0) {
k = 4;
p = ctx_memory[4];
}
else if (p - ctx_memory[k] >= (1 << 21)) {
++k;
p = ctx_memory[k];
}
ctx[i]->memory = p;
p += cn_sizes[cn_indices[part]];
}
}
for (size_t i = 0; i < 5; ++i) {
for (size_t j = n; j < N; ++j) {
core_hash[core_indices[part * 5 + i]](input + j * input_size, input_size, tmp + j * 64);
}
input = tmp;
input_size = 64;
}
auto f = CnHash::fn(cn_hash[cn_indices[part]], av[t.step], Assembly::AUTO);
for (size_t j = n; j < N; j += t.step) {
f(tmp + j * 64, 64, output + j * 32, ctx + n, 0);
}
for (size_t j = n; j < N; ++j) {
memcpy(tmp + j * 64, output + j * 32, 32);
memset(tmp + j * 64 + 32, 0, 32);
}
}
});
const uint8_t* input = data;
size_t input_size = size;
for (size_t part = 0; part < 3; ++part) {
const AlgoTune& t = tune[cn_indices[part]];
// Allocate scratchpads
{
uint8_t* p = ctx_memory[0];
for (size_t i = 0, k = 0; i < n; ++i) {
if ((i % t.step) == 0) {
k = 0;
p = ctx_memory[0];
}
else if (p - ctx_memory[k] >= (1 << 21)) {
++k;
p = ctx_memory[k];
}
ctx[i]->memory = p;
p += cn_sizes[cn_indices[part]];
}
}
for (size_t i = 0; i < 5; ++i) {
for (size_t j = 0; j < n; ++j) {
core_hash[core_indices[part * 5 + i]](input + j * input_size, input_size, tmp + j * 64);
}
input = tmp;
input_size = 64;
}
auto f = CnHash::fn(cn_hash[cn_indices[part]], av[t.step], Assembly::AUTO);
for (size_t j = 0; j < n; j += t.step) {
f(tmp + j * 64, 64, output + j * 32, ctx, 0);
}
for (size_t j = 0; j < n; ++j) {
memcpy(tmp + j * 64, output + j * 32, 32);
memset(tmp + j * 64 + 32, 0, 32);
}
}
helper->wait();
}
else {
for (size_t part = 0; part < 3; ++part) {
const AlgoTune& t = tune[cn_indices[part]];
// Allocate scratchpads
{
uint8_t* p = ctx_memory[0];
const size_t n = N / t.threads;
// Thread 1
for (size_t i = 0, k = 0; i < n; ++i) {
if ((i % t.step) == 0) {
k = 0;
p = ctx_memory[0];
}
else if (p - ctx_memory[k] >= (1 << 21)) {
++k;
p = ctx_memory[k];
}
ctx[i]->memory = p;
p += cn_sizes[cn_indices[part]];
}
// Thread 2
for (size_t i = n, k = 4; i < N; ++i) {
if ((i % t.step) == 0) {
k = 4;
p = ctx_memory[4];
}
else if (p - ctx_memory[k] >= (1 << 21)) {
++k;
p = ctx_memory[k];
}
ctx[i]->memory = p;
p += cn_sizes[cn_indices[part]];
}
}
size_t n = N;
if (helper && (t.threads == 2)) {
n = N / 2;
helper->launch_task([data, size, n, &cn_indices, &core_indices, part, &tmp, av, &t, output, ctx]() {
const uint8_t* input = data;
size_t input_size = size;
for (size_t i = 0; i < 5; ++i) {
for (size_t j = n; j < N; ++j) {
core_hash[core_indices[part * 5 + i]](input + j * input_size, input_size, tmp + j * 64);
}
input = tmp;
input_size = 64;
}
auto f = CnHash::fn(cn_hash[cn_indices[part]], av[t.step], Assembly::AUTO);
for (size_t j = n; j < N; j += t.step) {
f(tmp + j * 64, 64, output + j * 32, ctx + n, 0);
}
for (size_t j = n; j < N; ++j) {
memcpy(tmp + j * 64, output + j * 32, 32);
memset(tmp + j * 64 + 32, 0, 32);
}
});
}
for (size_t i = 0; i < 5; ++i) {
for (size_t j = 0; j < n; ++j) {
core_hash[core_indices[part * 5 + i]](data + j * size, size, tmp + j * 64);
}
data = tmp;
size = 64;
}
auto f = CnHash::fn(cn_hash[cn_indices[part]], av[t.step], Assembly::AUTO);
for (size_t j = 0; j < n; j += t.step) {
f(tmp + j * 64, 64, output + j * 32, ctx, 0);
}
for (size_t j = 0; j < n; ++j) {
memcpy(tmp + j * 64, output + j * 32, 32);
memset(tmp + j * 64 + 32, 0, 32);
}
if (helper && (t.threads == 2)) {
helper->wait();
}
}
}
for (size_t i = 0; i < N; ++i) {
ctx[i]->memory = ctx_memory[i];
}
}
#else // XMRIG_FEATURE_HWLOC
void benchmark() {}
HelperThread* create_helper_thread(int64_t, const std::vector<int64_t>&) { return nullptr; }
void destroy_helper_thread(HelperThread*) {}
void hash_octa(const uint8_t* data, size_t size, uint8_t* output, cryptonight_ctx** ctx, HelperThread*)
{
constexpr uint32_t N = 8;
// PrevBlockHash (GhostRider's seed) is stored in bytes [4; 36)
const uint8_t* seed = data + 4;
uint32_t core_indices[15];
select_indices(core_indices, seed);
uint32_t cn_indices[6];
select_indices(cn_indices, seed);
#ifdef XMRIG_ARM
uint32_t step[6] = { 1, 1, 1, 1, 1, 2 };
#else
uint32_t step[6] = { 4, 4, 1, 2, 4, 4 };
#endif
static uint32_t prev_indices[3];
if (memcmp(cn_indices, prev_indices, sizeof(prev_indices)) != 0) {
memcpy(prev_indices, cn_indices, sizeof(prev_indices));
for (int i = 0; i < 3; ++i) {
LOG_INFO("%s GhostRider algo %d: %s", Tags::cpu(), i + 1, cn_names[cn_indices[i]]);
}
}
const CnHash::AlgoVariant* av = Cpu::info()->hasAES() ? av_hw_aes : av_soft_aes;
const cn_hash_fun f[3] = {
CnHash::fn(cn_hash[cn_indices[0]], av[step[cn_indices[0]]], Assembly::AUTO),
CnHash::fn(cn_hash[cn_indices[1]], av[step[cn_indices[1]]], Assembly::AUTO),
CnHash::fn(cn_hash[cn_indices[2]], av[step[cn_indices[2]]], Assembly::AUTO),
};
uint8_t tmp[64 * N];
for (uint64_t part = 0; part < 3; ++part) {
for (uint64_t i = 0; i < 5; ++i) {
for (uint64_t j = 0; j < N; ++j) {
core_hash[core_indices[part * 5 + i]](data + j * size, size, tmp + j * 64);
data = tmp;
size = 64;
}
}
for (uint64_t j = 0, k = step[cn_indices[part]]; j < N; j += k) {
f[part](tmp + j * 64, 64, output + j * 32, ctx, 0);
}
for (uint64_t j = 0; j < N; ++j) {
memcpy(tmp + j * 64, output + j * 32, 32);
memset(tmp + j * 64 + 32, 0, 32);
}
}
}
#endif // XMRIG_FEATURE_HWLOC
} // namespace ghostrider
} // namespace xmrig