randomx_cache and randomx_dataset changed to standard-layout structs

This commit is contained in:
tevador 2019-04-28 12:44:28 +02:00
parent fd7186f873
commit 22a3aa8d79
19 changed files with 155 additions and 173 deletions

View File

@ -113,6 +113,10 @@ namespace randomx {
typedef void(*DatasetReadFunc)(addr_t, MemoryRegisters&, int_reg_t(&reg)[RegistersCount]);
typedef void(*ProgramFunc)(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t);
typedef void(*DatasetInitFunc)(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock);
typedef void(*DatasetDeallocFunc)(randomx_dataset*);
typedef void(*CacheDeallocFunc)(randomx_cache*);
typedef void(*CacheInitializeFunc)(randomx_cache*, const void*, size_t);
}
std::ostream& operator<<(std::ostream& os, const randomx::RegisterFile& rf);

View File

@ -40,126 +40,84 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "argon2.h"
#include "argon2_core.h"
randomx_dataset::~randomx_dataset() {
}
static_assert(RANDOMX_ARGON_MEMORY % (RANDOMX_ARGON_LANES * ARGON2_SYNC_POINTS) == 0, "RANDOMX_ARGON_MEMORY - invalid value");
static_assert(ARGON2_BLOCK_SIZE == randomx::ArgonBlockSize, "Unpexpected value of ARGON2_BLOCK_SIZE");
void randomx_cache::initialize(const void *seed, size_t seedSize) {
uint32_t memory_blocks, segment_length;
argon2_instance_t instance;
argon2_context context;
namespace randomx {
context.out = nullptr;
context.outlen = 0;
context.pwd = CONST_CAST(uint8_t *)seed;
context.pwdlen = (uint32_t)seedSize;
context.salt = CONST_CAST(uint8_t *)RANDOMX_ARGON_SALT;
context.saltlen = (uint32_t)randomx::ArgonSaltSize;
context.secret = NULL;
context.secretlen = 0;
context.ad = NULL;
context.adlen = 0;
context.t_cost = RANDOMX_ARGON_ITERATIONS;
context.m_cost = RANDOMX_ARGON_MEMORY;
context.lanes = RANDOMX_ARGON_LANES;
context.threads = 1;
context.allocate_cbk = NULL;
context.free_cbk = NULL;
context.flags = ARGON2_DEFAULT_FLAGS;
context.version = ARGON2_VERSION_NUMBER;
void initCache(randomx_cache* cache, const void* seed, size_t seedSize) {
uint32_t memory_blocks, segment_length;
argon2_instance_t instance;
argon2_context context;
/* 2. Align memory size */
/* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
memory_blocks = context.m_cost;
context.out = nullptr;
context.outlen = 0;
context.pwd = CONST_CAST(uint8_t *)seed;
context.pwdlen = (uint32_t)seedSize;
context.salt = CONST_CAST(uint8_t *)RANDOMX_ARGON_SALT;
context.saltlen = (uint32_t)randomx::ArgonSaltSize;
context.secret = NULL;
context.secretlen = 0;
context.ad = NULL;
context.adlen = 0;
context.t_cost = RANDOMX_ARGON_ITERATIONS;
context.m_cost = RANDOMX_ARGON_MEMORY;
context.lanes = RANDOMX_ARGON_LANES;
context.threads = 1;
context.allocate_cbk = NULL;
context.free_cbk = NULL;
context.flags = ARGON2_DEFAULT_FLAGS;
context.version = ARGON2_VERSION_NUMBER;
segment_length = memory_blocks / (context.lanes * ARGON2_SYNC_POINTS);
/* 2. Align memory size */
/* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
memory_blocks = context.m_cost;
instance.version = context.version;
instance.memory = NULL;
instance.passes = context.t_cost;
instance.memory_blocks = memory_blocks;
instance.segment_length = segment_length;
instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
instance.lanes = context.lanes;
instance.threads = context.threads;
instance.type = Argon2_d;
instance.memory = (block*)memory;
segment_length = memory_blocks / (context.lanes * ARGON2_SYNC_POINTS);
if (instance.threads > instance.lanes) {
instance.threads = instance.lanes;
}
instance.version = context.version;
instance.memory = NULL;
instance.passes = context.t_cost;
instance.memory_blocks = memory_blocks;
instance.segment_length = segment_length;
instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
instance.lanes = context.lanes;
instance.threads = context.threads;
instance.type = Argon2_d;
instance.memory = (block*)cache->memory;
/* 3. Initialization: Hashing inputs, allocating memory, filling first
* blocks
*/
argon_initialize(&instance, &context);
if (instance.threads > instance.lanes) {
instance.threads = instance.lanes;
}
fill_memory_blocks(&instance);
/* 3. Initialization: Hashing inputs, allocating memory, filling first
* blocks
*/
argon_initialize(&instance, &context);
reciprocalCache.clear();
randomx::Blake2Generator gen(seed, seedSize);
for (int i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
randomx::generateSuperscalar(programs[i], gen);
for (unsigned j = 0; j < programs[i].getSize(); ++j) {
auto& instr = programs[i](j);
if (instr.opcode == randomx::SuperscalarInstructionType::IMUL_RCP) {
auto rcp = randomx_reciprocal(instr.getImm32());
instr.setImm32(reciprocalCache.size());
reciprocalCache.push_back(rcp);
fill_memory_blocks(&instance);
cache->reciprocalCache.clear();
randomx::Blake2Generator gen(seed, seedSize);
for (int i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
randomx::generateSuperscalar(cache->programs[i], gen);
for (unsigned j = 0; j < cache->programs[i].getSize(); ++j) {
auto& instr = cache->programs[i](j);
if (instr.opcode == randomx::SuperscalarInstructionType::IMUL_RCP) {
auto rcp = randomx_reciprocal(instr.getImm32());
instr.setImm32(cache->reciprocalCache.size());
cache->reciprocalCache.push_back(rcp);
}
}
}
}
}
namespace randomx {
template<class Allocator>
void Dataset<Allocator>::allocate() {
memory = (uint8_t*)Allocator::allocMemory(DatasetSize);
void initCacheCompile(randomx_cache* cache, const void* seed, size_t seedSize) {
initCache(cache, seed, seedSize);
cache->jit->generateSuperscalarHash(cache->programs, cache->reciprocalCache);
cache->jit->generateDatasetInitCode();
}
template<class Allocator>
Dataset<Allocator>::~Dataset() {
Allocator::freeMemory(memory, DatasetSize);
}
template<class Allocator>
void Cache<Allocator>::allocate() {
memory = (uint8_t*)Allocator::allocMemory(CacheSize);
}
template<class Allocator>
Cache<Allocator>::~Cache() {
Allocator::freeMemory(memory, CacheSize);
}
template<class Allocator>
DatasetInitFunc Cache<Allocator>::getInitFunc() {
return &initDataset;
}
template<class Allocator>
DatasetInitFunc CacheWithJit<Allocator>::getInitFunc() {
return jit.getDatasetInitFunc();
}
template<class Allocator>
void CacheWithJit<Allocator>::initialize(const void *seed, size_t seedSize) {
randomx_cache::initialize(seed, seedSize);
jit.generateSuperscalarHash(programs, reciprocalCache);
jit.generateDatasetInitCode();
}
template class Dataset<AlignedAllocator<CacheLineSize>>;
template class Dataset<LargePageAllocator>;
template class Cache<AlignedAllocator<CacheLineSize>>;
template class Cache<LargePageAllocator>;
template class CacheWithJit<AlignedAllocator<CacheLineSize>>;
template class CacheWithJit<LargePageAllocator>;
constexpr uint64_t superscalarMul0 = 6364136223846793005ULL;
constexpr uint64_t superscalarAdd1 = 9298411001130361340ULL;
constexpr uint64_t superscalarAdd2 = 12065312585734608966ULL;

View File

@ -21,6 +21,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <cstdint>
#include <vector>
#include <type_traits>
#include "common.hpp"
#include "superscalar_program.hpp"
#include "jit_compiler_x86.hpp"
@ -28,51 +29,45 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
/* Global scope for C binding */
struct randomx_dataset {
virtual ~randomx_dataset() = 0;
virtual void allocate() = 0;
uint8_t* memory = nullptr;
randomx::DatasetDeallocFunc dealloc;
};
/* Global scope for C binding */
struct randomx_cache : public randomx_dataset {
virtual randomx::DatasetInitFunc getInitFunc() = 0;
virtual void initialize(const void *seed, size_t seedSize);
struct randomx_cache {
uint8_t* memory = nullptr;
randomx::CacheDeallocFunc dealloc;
randomx::JitCompilerX86* jit;
randomx::CacheInitializeFunc initialize;
randomx::DatasetInitFunc datasetInit;
randomx::SuperscalarProgram programs[RANDOMX_CACHE_ACCESSES];
std::vector<uint64_t> reciprocalCache;
};
//A pointer to a standard-layout struct object points to its initial member
static_assert(std::is_standard_layout<randomx_dataset>(), "randomx_dataset must be a standard-layout struct");
static_assert(std::is_standard_layout<randomx_cache>(), "randomx_cache must be a standard-layout struct");
namespace randomx {
template<class Allocator>
struct Dataset : public randomx_dataset {
~Dataset() override;
void allocate() override;
};
using DatasetDefault = Dataset<AlignedAllocator<CacheLineSize>>;
using DatasetLargePage = Dataset<LargePageAllocator>;
using DefaultAllocator = AlignedAllocator<CacheLineSize>;
template<class Allocator>
struct Cache : public randomx_cache {
~Cache() override;
void allocate() override;
DatasetInitFunc getInitFunc() override;
};
void deallocDataset(randomx_dataset* dataset) {
if (dataset->memory != nullptr)
Allocator::freeMemory(dataset->memory, DatasetSize);
}
template<class Allocator>
struct CacheWithJit : public Cache<Allocator> {
using Cache<Allocator>::programs;
using Cache<Allocator>::reciprocalCache;
void initialize(const void *seed, size_t seedSize) override;
DatasetInitFunc getInitFunc() override;
JitCompilerX86 jit;
};
using CacheDefault = Cache<AlignedAllocator<CacheLineSize>>;
using CacheWithJitDefault = CacheWithJit<AlignedAllocator<CacheLineSize>>;
using CacheLargePage = Cache<LargePageAllocator>;
using CacheWithJitLargePage = CacheWithJit<LargePageAllocator>;
void deallocCache(randomx_cache* cache) {
if(cache->memory != nullptr)
Allocator::freeMemory(cache->memory, CacheSize);
if (cache->jit != nullptr)
delete cache->jit;
}
void initCache(randomx_cache*, const void*, size_t);
void initCacheCompile(randomx_cache*, const void*, size_t);
void initDatasetItem(randomx_cache* cache, uint8_t* out, uint64_t blockNumber);
void initDataset(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock);
}

View File

@ -21,6 +21,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <cstdint>
#include <iostream>
#include <type_traits>
#include "blake2/endian.h"
namespace randomx {
@ -93,10 +94,9 @@ namespace randomx {
uint8_t opcode;
uint8_t dst;
uint8_t src;
private:
uint8_t mod;
uint32_t imm32;
private:
void print(std::ostream&) const;
static const char* names[256];
static InstructionFormatter engine[256];
@ -136,5 +136,5 @@ namespace randomx {
};
static_assert(sizeof(Instruction) == 8, "Invalid size of struct randomx::Instruction");
static_assert(std::is_standard_layout<Instruction>(), "randomx::Instruction must be a standard-layout struct");
}

View File

@ -89,7 +89,7 @@ DECL(randomx_dataset_init):
push r13
push r14
push r15
mov rdi, qword ptr [rdi+8] ;# after virtual method table pointer
mov rdi, qword ptr [rdi] ;# cache->memory
;# dataset in rsi
mov rbp, rdx ;# block index
push rcx ;# max. block index

View File

@ -92,7 +92,7 @@ randomx_dataset_init PROC
push r13
push r14
push r15
mov rdi, qword ptr [rcx+8] ;# after virtual method table pointer
mov rdi, qword ptr [rcx] ;# cache->memory
mov rsi, rdx ;# dataset
mov rbp, r8 ;# block index
push r9 ;# max. block index

View File

@ -28,34 +28,48 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
extern "C" {
randomx_cache *randomx_alloc_cache(randomx_flags flags) {
randomx_cache *cache = nullptr;
randomx_cache *cache = new randomx_cache();
try {
switch (flags & (RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES)) {
case RANDOMX_FLAG_DEFAULT:
cache = new randomx::CacheDefault();
cache->dealloc = &randomx::deallocCache<randomx::DefaultAllocator>;
cache->jit = nullptr;
cache->initialize = &randomx::initCache;
cache->datasetInit = &randomx::initDataset;
cache->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(randomx::CacheSize);
break;
case RANDOMX_FLAG_JIT:
cache = new randomx::CacheWithJitDefault();
cache->dealloc = &randomx::deallocCache<randomx::DefaultAllocator>;
cache->jit = new randomx::JitCompilerX86();
cache->initialize = &randomx::initCacheCompile;
cache->datasetInit = cache->jit->getDatasetInitFunc();
cache->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(randomx::CacheSize);
break;
case RANDOMX_FLAG_LARGE_PAGES:
cache = new randomx::CacheLargePage();
cache->dealloc = &randomx::deallocCache<randomx::LargePageAllocator>;
cache->jit = nullptr;
cache->initialize = &randomx::initCache;
cache->datasetInit = &randomx::initDataset;
cache->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(randomx::CacheSize);
break;
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES:
cache = new randomx::CacheWithJitLargePage();
cache->dealloc = &randomx::deallocCache<randomx::LargePageAllocator>;
cache->jit = new randomx::JitCompilerX86();
cache->initialize = &randomx::initCacheCompile;
cache->datasetInit = cache->jit->getDatasetInitFunc();
cache->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(randomx::CacheSize);
break;
default:
UNREACHABLE;
}
cache->allocate();
}
catch (std::exception &ex) {
delete cache;
randomx_release_cache(cache);
cache = nullptr;
}
@ -63,27 +77,29 @@ extern "C" {
}
void randomx_init_cache(randomx_cache *cache, const void *seed, size_t seedSize) {
cache->initialize(seed, seedSize);
cache->initialize(cache, seed, seedSize);
}
void randomx_release_cache(randomx_cache* cache) {
cache->dealloc(cache);
delete cache;
}
randomx_dataset *randomx_alloc_dataset(randomx_flags flags) {
randomx_dataset *dataset = nullptr;
randomx_dataset *dataset = new randomx_dataset();
try {
if (flags & RANDOMX_FLAG_LARGE_PAGES) {
dataset = new randomx::DatasetLargePage();
dataset->dealloc = &randomx::deallocDataset<randomx::LargePageAllocator>;
dataset->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(randomx::DatasetSize);
}
else {
dataset = new randomx::DatasetDefault();
dataset->dealloc = &randomx::deallocDataset<randomx::DefaultAllocator>;
dataset->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(randomx::DatasetSize);
}
dataset->allocate();
}
catch (std::exception &ex) {
delete dataset;
randomx_release_dataset(dataset);
dataset = nullptr;
}
@ -95,8 +111,7 @@ extern "C" {
}
void randomx_init_dataset(randomx_dataset *dataset, randomx_cache *cache, unsigned long startItem, unsigned long itemCount) {
randomx::DatasetInitFunc dsfunc = cache->getInitFunc();
dsfunc(cache, dataset->memory + startItem * randomx::CacheLineSize, startItem, startItem + itemCount);
cache->datasetInit(cache, dataset->memory + startItem * randomx::CacheLineSize, startItem, startItem + itemCount);
}
void *randomx_get_dataset_memory(randomx_dataset *dataset) {
@ -104,6 +119,7 @@ extern "C" {
}
void randomx_release_dataset(randomx_dataset *dataset) {
dataset->dealloc(dataset);
delete dataset;
}

View File

@ -46,6 +46,10 @@ namespace randomx {
void setAddressRegister(uint32_t val) {
addrReg = val;
}
Instruction programBuffer[RANDOMX_SUPERSCALAR_MAX_SIZE];
uint32_t size;
int addrReg;
double ipc;
int codeSize;
int macroOps;
@ -62,9 +66,6 @@ namespace randomx {
os << instr;
}
}
Instruction programBuffer[RANDOMX_SUPERSCALAR_MAX_SIZE];
uint32_t size;
int addrReg;
};
}

View File

@ -165,15 +165,13 @@ int main(int argc, char** argv) {
Stopwatch sw(true);
cache = randomx_alloc_cache(flags);
if (cache == nullptr) {
std::cout << "ERROR: Cache allocation failed" << std::endl;
return 1;
throw std::runtime_error("Cache allocation failed");
}
randomx_init_cache(cache, &seed, sizeof(seed));
if (miningMode) {
dataset = randomx_alloc_dataset(flags);
if (dataset == nullptr) {
std::cout << "ERROR: Dataset allocation failed" << std::endl;
return 1;
throw std::runtime_error("Dataset allocation failed");
}
uint32_t datasetItemCount = randomx_dataset_item_count();
if (initThreadCount > 1) {
@ -200,8 +198,7 @@ int main(int argc, char** argv) {
for (int i = 0; i < threadCount; ++i) {
randomx_vm *vm = randomx_create_vm(flags, cache, dataset);
if (vm == nullptr) {
std::cout << "ERROR: Unsupported virtual machine options" << std::endl;
return 1;
throw std::runtime_error("Unsupported virtual machine options");
}
vms.push_back(vm);
}
@ -221,7 +218,14 @@ int main(int argc, char** argv) {
else {
mine(vms[0], std::ref(atomicNonce), std::ref(result), noncesCount, 0);
}
double elapsed = sw.getElapsed();
for (unsigned i = 0; i < vms.size(); ++i)
randomx_destroy_vm(vms[i]);
if (miningMode)
randomx_release_dataset(dataset);
else
randomx_release_cache(cache);
std::cout << "Calculated result: ";
result.print(std::cout);
if (noncesCount == 1000 && seedValue == 0)

View File

@ -103,7 +103,7 @@ namespace randomx {
template<class Allocator, bool softAes>
void VmBase<Allocator, softAes>::allocate() {
if (mem.memory == nullptr)
if (datasetPtr == nullptr)
throw std::invalid_argument("Cache/Dataset not set");
if (!softAes) { //if hardware AES is not supported, it's better to fail now than to return a ticking bomb
__m128i tmp = _mm_load_si128((const __m128i*)&aesDummy);

View File

@ -44,7 +44,10 @@ protected:
alignas(16) randomx::ProgramConfiguration config;
randomx::MemoryRegisters mem;
uint8_t* scratchpad;
uint8_t* datasetBasePtr;
union {
randomx_cache* cachePtr = nullptr;
randomx_dataset* datasetPtr;
};
uint32_t datasetOffset;
};

View File

@ -27,8 +27,7 @@ namespace randomx {
template<class Allocator, bool softAes>
void CompiledVm<Allocator, softAes>::setDataset(randomx_dataset* dataset) {
mem.memory = dataset->memory;
datasetBasePtr = dataset->memory;
datasetPtr = dataset;
}
template<class Allocator, bool softAes>
@ -36,7 +35,7 @@ namespace randomx {
VmBase<Allocator, softAes>::generateProgram(seed);
randomx_vm::initialize();
compiler.generateProgram(program, config);
mem.memory = datasetBasePtr + datasetOffset;
mem.memory = datasetPtr->memory + datasetOffset;
execute();
}

View File

@ -48,7 +48,7 @@ namespace randomx {
using VmBase<Allocator, softAes>::config;
using VmBase<Allocator, softAes>::reg;
using VmBase<Allocator, softAes>::scratchpad;
using VmBase<Allocator, softAes>::datasetBasePtr;
using VmBase<Allocator, softAes>::datasetPtr;
using VmBase<Allocator, softAes>::datasetOffset;
protected:
void execute();

View File

@ -25,6 +25,7 @@ namespace randomx {
template<class Allocator, bool softAes>
void CompiledLightVm<Allocator, softAes>::setCache(randomx_cache* cache) {
cachePtr = cache;
mem.memory = cache->memory;
compiler.generateSuperscalarHash(cache->programs, cache->reciprocalCache);
}

View File

@ -44,6 +44,7 @@ namespace randomx {
using CompiledVm<Allocator, softAes>::compiler;
using CompiledVm<Allocator, softAes>::program;
using CompiledVm<Allocator, softAes>::config;
using CompiledVm<Allocator, softAes>::cachePtr;
using CompiledVm<Allocator, softAes>::datasetOffset;
};

View File

@ -45,6 +45,7 @@ namespace randomx {
template<class Allocator, bool softAes>
void InterpretedVm<Allocator, softAes>::setDataset(randomx_dataset* dataset) {
datasetPtr = dataset;
mem.memory = dataset->memory;
}

View File

@ -57,7 +57,7 @@ namespace randomx {
using VmBase<Allocator, softAes>::program;
using VmBase<Allocator, softAes>::config;
using VmBase<Allocator, softAes>::reg;
using VmBase<Allocator, softAes>::datasetBasePtr;
using VmBase<Allocator, softAes>::datasetPtr;
using VmBase<Allocator, softAes>::datasetOffset;
void* operator new(size_t size) {
void* ptr = AlignedAllocator<CacheLineSize>::allocMemory(size);

View File

@ -24,8 +24,8 @@ namespace randomx {
template<class Allocator, bool softAes>
void InterpretedLightVm<Allocator, softAes>::setCache(randomx_cache* cache) {
mem.memory = cache->memory;
cachePtr = cache;
mem.memory = cache->memory;
}
template<class Allocator, bool softAes>

View File

@ -28,6 +28,7 @@ namespace randomx {
class InterpretedLightVm : public InterpretedVm<Allocator, softAes> {
public:
using VmBase<Allocator, softAes>::mem;
using VmBase<Allocator, softAes>::cachePtr;
void* operator new(size_t size) {
void* ptr = AlignedAllocator<CacheLineSize>::allocMemory(size);
if (ptr == nullptr)
@ -41,8 +42,6 @@ namespace randomx {
void setCache(randomx_cache* cache) override;
protected:
void datasetRead(uint32_t address, int_reg_t(&r)[8]) override;
private:
randomx_cache* cachePtr;
};
using InterpretedLightVmDefault = InterpretedLightVm<AlignedAllocator<CacheLineSize>, true>;