mirror of
https://github.com/hinto-janai/cuprate.git
synced 2025-01-18 08:44:33 +00:00
random-x: add dataset/ cache
This commit is contained in:
parent
f9c2794af2
commit
545189f523
8 changed files with 158 additions and 31 deletions
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
[features]
|
[features]
|
||||||
default = ["jit"]
|
default = ["jit"]
|
||||||
jit = ["dep:dynasmrt"]
|
jit = ["dep:dynasmrt"]
|
||||||
|
rayon = ["dep:rayon"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
blake2 = "0.10"
|
blake2 = "0.10"
|
||||||
|
@ -15,5 +16,7 @@ hex-literal = "0.4"
|
||||||
|
|
||||||
dynasmrt = {version = "2.0.0", optional = true}
|
dynasmrt = {version = "2.0.0", optional = true}
|
||||||
|
|
||||||
|
rayon = {version ="1.7", optional = true}
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 2
|
opt-level = 3
|
|
@ -1,17 +1,20 @@
|
||||||
use argon2::{Algorithm, Argon2, Block, Params, Version};
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
use argon2::{Algorithm, Argon2, Block, Params, Version};
|
||||||
|
#[cfg(feature = "rayon")]
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use crate::blake2_generator::Blake2Generator;
|
use crate::blake2_generator::Blake2Generator;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{
|
config::{
|
||||||
RANDOMX_ARGON_ITERATIONS, RANDOMX_ARGON_LANES, RANDOMX_ARGON_MEMORY, RANDOMX_ARGON_SALT,
|
RANDOMX_ARGON_ITERATIONS, RANDOMX_ARGON_LANES, RANDOMX_ARGON_MEMORY, RANDOMX_ARGON_SALT,
|
||||||
RANDOMX_CACHE_ACCESSES,
|
RANDOMX_CACHE_ACCESSES, RANDOMX_DATASET_SIZE,
|
||||||
},
|
},
|
||||||
|
registers::{RGroupRegisterID, RGroupRegisters},
|
||||||
superscalar::SSProgram,
|
superscalar::SSProgram,
|
||||||
};
|
};
|
||||||
|
|
||||||
trait Dataset {}
|
/// Generates the memory blocks used in the cache
|
||||||
|
|
||||||
fn argon2_blocks(key: &[u8]) -> Box<[Block]> {
|
fn argon2_blocks(key: &[u8]) -> Box<[Block]> {
|
||||||
let params = Params::new(
|
let params = Params::new(
|
||||||
RANDOMX_ARGON_MEMORY,
|
RANDOMX_ARGON_MEMORY,
|
||||||
|
@ -35,6 +38,31 @@ fn argon2_blocks(key: &[u8]) -> Box<[Block]> {
|
||||||
blocks
|
blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The Cache.
|
||||||
|
///
|
||||||
|
/// The cache is used during light verification.
|
||||||
|
/// Internally this struct is a wrapper around an [`Arc`] internal cache, this allows
|
||||||
|
/// cheep clones and allows the cache to be shared between VMs on different threads.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Cache {
|
||||||
|
internal_cache: Arc<RwLock<InternalCache>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cache {
|
||||||
|
/// Initialises the cache with the provided key.
|
||||||
|
///
|
||||||
|
/// The key must be between 1-60 bytes (inclusive) otherwise this will panic.
|
||||||
|
pub fn init(key: &[u8]) -> Self {
|
||||||
|
let internal_cache = InternalCache::init(key);
|
||||||
|
Cache {
|
||||||
|
internal_cache: Arc::new(RwLock::new(internal_cache)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The internal cache structure, used during light verification.
|
||||||
|
#[derive(Debug)]
|
||||||
|
|
||||||
struct InternalCache {
|
struct InternalCache {
|
||||||
memory_blocks: Box<[Block]>,
|
memory_blocks: Box<[Block]>,
|
||||||
programs: Vec<SSProgram>,
|
programs: Vec<SSProgram>,
|
||||||
|
@ -55,32 +83,111 @@ impl InternalCache {
|
||||||
programs,
|
programs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets an item from the cache at the specified index.
|
||||||
|
fn get_item(&self, idx: usize) -> [u64; 8] {
|
||||||
|
// one item is 8 u64s
|
||||||
|
// mask = (blocks in cache * bytes in a block / size of item) minus one.
|
||||||
|
let mask = (self.memory_blocks.len() * 1024 / 64) - 1;
|
||||||
|
// and the idx with the mask this is the same as doing mod (self.memory_blocks.len() * 1024 / 64)
|
||||||
|
let idx = idx & mask;
|
||||||
|
|
||||||
|
// block_idx = idx divided by amount of items in a block
|
||||||
|
let block_idx = idx / (1024 / 64);
|
||||||
|
// idx * 8 is to get the idx of a single u64
|
||||||
|
// we mask with amount of u64s in a block minus 1 which is the same as doing
|
||||||
|
// mod the amount of instructions in a block.
|
||||||
|
let block_u64_start = (idx * 8) & 127;
|
||||||
|
// The plus 8 cannot overflow as (idx * 8) & 127 wont give a number bigger than 120
|
||||||
|
return self.memory_blocks[block_idx].as_ref()[block_u64_start..block_u64_start + 8]
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the dataset item at the specified index.
|
||||||
|
fn init_data_set_item(&self, item_number: usize) -> [u64; 8] {
|
||||||
|
let mut registers = RGroupRegisters::default();
|
||||||
|
registers.set(
|
||||||
|
&RGroupRegisterID::R0,
|
||||||
|
(TryInto::<u64>::try_into(item_number).unwrap() + 1_u64)
|
||||||
|
.wrapping_mul(6364136223846793005_u64),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut init_reg = |dst, val: u64| {
|
||||||
|
registers.apply_to_dst_with_src(&dst, &RGroupRegisterID::R0, |_, src| src ^ val)
|
||||||
|
};
|
||||||
|
|
||||||
|
init_reg(RGroupRegisterID::R1, 9298411001130361340);
|
||||||
|
init_reg(RGroupRegisterID::R2, 12065312585734608966);
|
||||||
|
init_reg(RGroupRegisterID::R3, 9306329213124626780);
|
||||||
|
init_reg(RGroupRegisterID::R4, 5281919268842080866);
|
||||||
|
init_reg(RGroupRegisterID::R5, 10536153434571861004);
|
||||||
|
init_reg(RGroupRegisterID::R6, 3398623926847679864);
|
||||||
|
init_reg(RGroupRegisterID::R7, 9549104520008361294);
|
||||||
|
|
||||||
|
let mut cache_index = item_number;
|
||||||
|
|
||||||
|
for program in &self.programs {
|
||||||
|
program.execute(&mut registers);
|
||||||
|
|
||||||
|
let cache_item = self.get_item(cache_index);
|
||||||
|
for (reg_id, item) in RGroupRegisterID::iter().zip(cache_item) {
|
||||||
|
registers.apply_to_dst(®_id, |dst| dst ^ item);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_index = registers
|
||||||
|
.get(&program.reg_with_max_latency())
|
||||||
|
.try_into()
|
||||||
|
.expect("u64 does not fit into usize");
|
||||||
|
}
|
||||||
|
registers.inner()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cache {
|
/// The Dataset used during mining.
|
||||||
internal_cache: Arc<RwLock<InternalCache>>,
|
///
|
||||||
|
/// Internally this struct is a wrapper around an [`Arc`] internal dataset, this allows
|
||||||
|
/// cheep clones and allows the dataset to be shared between VMs on different threads.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Dataset {
|
||||||
|
internal_dataset: Arc<RwLock<InternalDataset>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dataset {
|
||||||
|
/// Initialises the dataset with the provided key.
|
||||||
|
///
|
||||||
|
/// The key must be between 1-60 bytes (inclusive) otherwise this will panic.
|
||||||
|
///
|
||||||
|
/// This is very computationally intense so might take a long time to complete.
|
||||||
|
pub fn init(key: &[u8]) -> Dataset {
|
||||||
|
let internal_dataset = InternalDataset::init(key);
|
||||||
|
Dataset {
|
||||||
|
internal_dataset: Arc::new(RwLock::new(internal_dataset)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The internal dataset used during mining.
|
||||||
|
#[derive(Debug)]
|
||||||
struct InternalDataset {
|
struct InternalDataset {
|
||||||
dataset: Vec<u64>,
|
dataset: Vec<[u64; 8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InternalDataset {
|
impl InternalDataset {
|
||||||
fn init(key: &[u8]) -> InternalDataset {
|
fn init(key: &[u8]) -> InternalDataset {
|
||||||
let cache = InternalCache::init(key);
|
let cache = InternalCache::init(key);
|
||||||
let
|
|
||||||
|
|
||||||
|
#[cfg(feature = "rayon")]
|
||||||
|
let dataset: Vec<[u64; 8]> = (0..RANDOMX_DATASET_SIZE / (64 * 8))
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|i| cache.init_data_set_item(i))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rayon"))]
|
||||||
|
let dataset: Vec<[u64; 8]> = (0..RANDOMX_DATASET_SIZE / (64 * 8))
|
||||||
|
.map(|i| cache.init_data_set_item(i))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Self { dataset }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_data_set_item(cache: &InternalCache, item: u64) -> [u64; 8] {
|
|
||||||
let
|
|
||||||
}
|
|
||||||
|
|
||||||
// 12118971377224777581
|
|
||||||
#[test]
|
|
||||||
fn init() {
|
|
||||||
let mem = InternalCache::init(&[5]);
|
|
||||||
|
|
||||||
println!("{:?}", mem.memory_blocks[1000])
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
mod aes_hash;
|
mod aes_hash;
|
||||||
mod blake2_generator;
|
mod blake2_generator;
|
||||||
mod config;
|
mod config;
|
||||||
//mod dataset;
|
mod dataset;
|
||||||
mod registers;
|
mod registers;
|
||||||
mod superscalar;
|
mod superscalar;
|
||||||
|
|
||||||
|
pub use dataset::{Cache, Dataset};
|
||||||
|
|
||||||
fn is_0_or_power_of_2(x: u64) -> bool {
|
fn is_0_or_power_of_2(x: u64) -> bool {
|
||||||
(x & (x - 1)) == 0
|
(x & (x - 1)) == 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,10 @@ impl RGroupRegisterID {
|
||||||
pub struct RGroupRegisters([u64; 8]);
|
pub struct RGroupRegisters([u64; 8]);
|
||||||
|
|
||||||
impl RGroupRegisters {
|
impl RGroupRegisters {
|
||||||
|
pub fn inner(self) -> [u64; 8] {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_to_dst(&mut self, dst: &RGroupRegisterID, f: impl FnOnce(u64) -> u64) {
|
pub fn apply_to_dst(&mut self, dst: &RGroupRegisterID, f: impl FnOnce(u64) -> u64) {
|
||||||
*self.get_mut(dst) = f(self.get(dst));
|
*self.get_mut(dst) = f(self.get(dst));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,16 @@ mod cpu;
|
||||||
mod executor;
|
mod executor;
|
||||||
mod generator;
|
mod generator;
|
||||||
mod instructions;
|
mod instructions;
|
||||||
|
mod program;
|
||||||
|
|
||||||
use crate::blake2_generator::Blake2Generator;
|
use crate::blake2_generator::Blake2Generator;
|
||||||
|
|
||||||
use crate::registers::RGroupRegisterID;
|
use crate::registers::{RGroupRegisterID, RGroupRegisters};
|
||||||
|
use executor::execute;
|
||||||
use generator::generate;
|
use generator::generate;
|
||||||
use instructions::ScalarInstruction;
|
use instructions::ScalarInstruction;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct SSProgram {
|
pub(crate) struct SSProgram {
|
||||||
program: Vec<ScalarInstruction>,
|
program: Vec<ScalarInstruction>,
|
||||||
reg_with_max_latency: RGroupRegisterID,
|
reg_with_max_latency: RGroupRegisterID,
|
||||||
|
@ -18,4 +21,12 @@ impl SSProgram {
|
||||||
pub fn generate(gen: &mut Blake2Generator) -> Self {
|
pub fn generate(gen: &mut Blake2Generator) -> Self {
|
||||||
generate(gen)
|
generate(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn execute(&self, registers: &mut RGroupRegisters) {
|
||||||
|
execute(&self.program, registers)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reg_with_max_latency(&self) -> RGroupRegisterID {
|
||||||
|
self.reg_with_max_latency
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ impl AllowedPorts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
pub enum MacroOp {
|
pub enum MacroOp {
|
||||||
SUB_RR,
|
SUB_RR,
|
||||||
XOR_RR,
|
XOR_RR,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::config::SUPERSCALAR_MAX_SIZE;
|
use crate::config::SUPERSCALAR_MAX_SIZE;
|
||||||
use crate::registers::{RGroupRegisterID, RGroupRegisters};
|
use crate::registers::{RGroupRegisterID, RGroupRegisters};
|
||||||
use crate::superscalar::cpu::{MacroOp, ProgramSchedule, SlotLen};
|
use crate::superscalar::cpu::{ProgramSchedule, SlotLen};
|
||||||
use crate::superscalar::instructions::ScalarInstruction;
|
use crate::superscalar::instructions::ScalarInstruction;
|
||||||
use crate::superscalar::SSProgram;
|
use crate::superscalar::SSProgram;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -497,7 +497,7 @@ enum ScalarInstructionBuilderSM {
|
||||||
macro_op_idx: usize,
|
macro_op_idx: usize,
|
||||||
},
|
},
|
||||||
/// NULL state, this state will only be finished on is the program is full.
|
/// NULL state, this state will only be finished on is the program is full.
|
||||||
NULL,
|
Null,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScalarInstructionBuilderSM {
|
impl ScalarInstructionBuilderSM {
|
||||||
|
@ -510,8 +510,8 @@ impl ScalarInstructionBuilderSM {
|
||||||
program_state: &mut ProgramState,
|
program_state: &mut ProgramState,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
match std::mem::replace(self, ScalarInstructionBuilderSM::NULL) {
|
match std::mem::replace(self, ScalarInstructionBuilderSM::Null) {
|
||||||
ScalarInstructionBuilderSM::NULL => {
|
ScalarInstructionBuilderSM::Null => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ScalarInstructionBuilderSM::Generate { .. } => {
|
ScalarInstructionBuilderSM::Generate { .. } => {
|
||||||
|
@ -682,8 +682,7 @@ impl ScalarInstructionBuilderSM {
|
||||||
|
|
||||||
let mut set = false;
|
let mut set = false;
|
||||||
for _ in 0..LOOK_FORWARD_CYCLES {
|
for _ in 0..LOOK_FORWARD_CYCLES {
|
||||||
if !builder.select_destination(gen, *scheduled_cycle, allow_chain_mul, ®isters_info)
|
if !builder.select_destination(gen, *scheduled_cycle, allow_chain_mul, registers_info) {
|
||||||
{
|
|
||||||
*scheduled_cycle += 1;
|
*scheduled_cycle += 1;
|
||||||
*cycle += 1;
|
*cycle += 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -731,7 +730,7 @@ impl ScalarInstructionBuilderSM {
|
||||||
match self {
|
match self {
|
||||||
ScalarInstructionBuilderSM::Generate { last_instruction } => *last_instruction,
|
ScalarInstructionBuilderSM::Generate { last_instruction } => *last_instruction,
|
||||||
ScalarInstructionBuilderSM::PartiallyComplete { builder, .. } => Some(builder.id),
|
ScalarInstructionBuilderSM::PartiallyComplete { builder, .. } => Some(builder.id),
|
||||||
ScalarInstructionBuilderSM::NULL => {
|
ScalarInstructionBuilderSM::Null => {
|
||||||
panic!("Should not be calling this function in this state")
|
panic!("Should not be calling this function in this state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::registers::RGroupRegisterID;
|
use crate::registers::RGroupRegisterID;
|
||||||
use crate::superscalar::cpu::{MacroOp, SlotLen};
|
use crate::superscalar::cpu::MacroOp;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
|
Loading…
Reference in a new issue