Skip to content

Commit

Permalink
Merge pull request #55 from 0xPolygonHermez/fractasy_implement_stats
Browse files Browse the repository at this point in the history
Implement Emu stats
  • Loading branch information
fractasy authored Aug 13, 2024
2 parents bab7f69 + 8d2533a commit c3d6df4
Show file tree
Hide file tree
Showing 10 changed files with 388 additions and 126 deletions.
9 changes: 2 additions & 7 deletions core/src/zisk_inst_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub const INVALID_VALUE_S64: i64 = 0xFFFFFFFFFFFFFFF_i64;
const INITIAL_VALUE: u64 = INVALID_VALUE;
const INITIAL_VALUE_S64: i64 = INVALID_VALUE_S64;

#[derive(Debug)]
pub struct ZiskInstBuilder {
ind_width_set: bool,
pub i: ZiskInst,
Expand Down Expand Up @@ -193,13 +194,7 @@ impl ZiskInstBuilder {

pub fn op(&mut self, optxt: &str) {
let op = op_from_str(optxt);
if op.t == "i" {
self.i.is_external_op = false;
} else if op.t == "e" {
self.i.is_external_op = true;
} else {
panic!("ZiskInstBuilder::op() found invalid op={}", optxt);
}
self.i.is_external_op = op.t != "i";
self.i.op = op.c;
self.i.op_str = op.n;
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/zisk_operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub struct ZiskOperation {
pub n: &'static str,
/// Operation type
pub t: &'static str,
/// Operation steps
pub s: u64,
/// Operation code (1 byte)
pub c: u8,
/// Operation function f(a,b)->(c,flag), where a, b, and c are 32-bit represented as 64-bit
Expand Down
263 changes: 163 additions & 100 deletions core/src/zisk_operations.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion core/src/zisk_rom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl RoData {
}

/// ZisK ROM data, including a map address to ZisK instruction
#[derive(Default)]
#[derive(Default, Debug)]
pub struct ZiskRom {
pub next_init_inst_addr: u64,
pub insts: HashMap<u64, ZiskInstBuilder>,
Expand Down
34 changes: 33 additions & 1 deletion emulator/src/emu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ impl<'a> Emu<'a> {
addr += self.ctx.sp;
}
self.ctx.a = self.ctx.mem.read(addr, 8);
if self.ctx.do_stats {
self.ctx.stats.on_memory_read(addr, 8);
}
}
SRC_IMM => self.ctx.a = instruction.a_offset_imm0 | (instruction.a_use_sp_imm1 << 32),
SRC_STEP => self.ctx.a = self.ctx.step,
Expand All @@ -72,6 +75,9 @@ impl<'a> Emu<'a> {
addr += self.ctx.sp;
}
self.ctx.b = self.ctx.mem.read(addr, 8);
if self.ctx.do_stats {
self.ctx.stats.on_memory_read(addr, 8);
}
}
SRC_IMM => self.ctx.b = instruction.b_offset_imm0 | (instruction.b_use_sp_imm1 << 32),
SRC_IND => {
Expand All @@ -80,6 +86,9 @@ impl<'a> Emu<'a> {
addr += self.ctx.sp;
}
self.ctx.b = self.ctx.mem.read(addr, instruction.ind_width);
if self.ctx.do_stats {
self.ctx.stats.on_memory_read(addr, instruction.ind_width);
}
}
_ => panic!("Emu::source_b() Invalid b_src={} pc={}", instruction.b_src, self.ctx.pc),
}
Expand All @@ -101,6 +110,9 @@ impl<'a> Emu<'a> {
addr += self.ctx.sp as i64;
}
self.ctx.mem.write(addr as u64, val as u64, 8);
if self.ctx.do_stats {
self.ctx.stats.on_memory_write(addr as u64, 8);
}
}
STORE_IND => {
let val: i64 = if instruction.store_ra {
Expand All @@ -114,6 +126,9 @@ impl<'a> Emu<'a> {
}
addr += self.ctx.a as i64;
self.ctx.mem.write(addr as u64, val as u64, instruction.ind_width);
if self.ctx.do_stats {
self.ctx.stats.on_memory_write(addr as u64, instruction.ind_width);
}
}
_ => panic!("Emu::store_c() Invalid store={} pc={}", instruction.store, self.ctx.pc),
}
Expand Down Expand Up @@ -195,6 +210,9 @@ impl<'a> Emu<'a> {

// Call the operation
(self.ctx.c, self.ctx.flag) = (instruction.func)(self.ctx.a, self.ctx.b);
if self.ctx.do_stats {
self.ctx.stats.on_op(instruction, self.ctx.a, self.ctx.b);
}

// Store the 'c' register value based on the storage specified by the current instruction
self.store_c(instruction);
Expand All @@ -206,7 +224,12 @@ impl<'a> Emu<'a> {
self.set_pc(instruction);

// If this is the last instruction, stop executing
self.ctx.end = instruction.end;
if instruction.end {
self.ctx.end = true;
if options.stats {
self.ctx.stats.on_steps(self.ctx.step);
}
}

// Log the step, if requested
#[cfg(debug_assertions)]
Expand Down Expand Up @@ -345,6 +368,9 @@ impl<'a> Emu<'a> {
}
//println!("Emu::run() full-equipe");

// Store the stats option into the emulator context
self.ctx.do_stats = options.stats;

// While not done
while !self.ctx.end {
if options.verbose {
Expand Down Expand Up @@ -406,6 +432,12 @@ impl<'a> Emu<'a> {

// println!("Emu::run() done ctx.pc={}", self.ctx.pc); // 2147483828
}

// Print stats report
if options.stats {
let report = self.ctx.stats.report();
println!("{}", report);
}
}

/// Run the whole program, fast
Expand Down
6 changes: 5 additions & 1 deletion emulator/src/emu_context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{EmuTrace, Mem};
use crate::{EmuTrace, Mem, Stats};
use zisk_core::{write_u64_le, INPUT_ADDR, MAX_INPUT_SIZE, RAM_ADDR, RAM_SIZE, ROM_ENTRY};

/// ZisK emulator context data container, storing the state of the emuulation
Expand All @@ -20,6 +20,8 @@ pub struct EmuContext {
pub callback_steps: u64,
pub last_callback_step: u64,
pub trace: EmuTrace,
pub do_stats: bool,
pub stats: Stats,
}

/// RisK emulator context implementation
Expand All @@ -44,6 +46,8 @@ impl EmuContext {
do_callback: false,
callback_steps: 0,
last_callback_step: 0,
do_stats: false,
stats: Stats::default(),
};

// Check the input data size is inside the proper range
Expand Down
3 changes: 3 additions & 0 deletions emulator/src/emu_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub struct EmuOptions {
/// Tracer v
#[clap(short = 'a', long, value_name = "TRACERV", default_value = "false")]
pub tracerv: bool,
#[clap(short = 'x', long, value_name = "STATS", default_value = "false")]
pub stats: bool,
}

/// Default constructor for impl fmt::Display for EmuOptions structure
Expand All @@ -72,6 +74,7 @@ impl Default for EmuOptions {
trace_steps: None,
log_metrics: false,
tracerv: false,
stats: false,
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions emulator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod emulator;
mod emulator_errors;
mod mem;
mod mem_section;
mod mem_trace;
mod stats;

pub use emu::*;
pub use emu_context::*;
Expand All @@ -18,4 +18,4 @@ pub use emulator::*;
pub use emulator_errors::*;
pub use mem::*;
pub use mem_section::*;
pub use mem_trace::*;
pub use stats::*;
14 changes: 0 additions & 14 deletions emulator/src/mem_trace.rs

This file was deleted.

177 changes: 177 additions & 0 deletions emulator/src/stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
use zisk_core::{ZiskInst, ZiskOperations};

const AREA_PER_SEC: f64 = 1000000_f64;
const COST_MEM: f64 = 10_f64 / AREA_PER_SEC;
const COST_MEMA_R1: f64 = 20_f64 / AREA_PER_SEC;
const COST_MEMA_R2: f64 = 40_f64 / AREA_PER_SEC;
const COST_MEMA_W1: f64 = 40_f64 / AREA_PER_SEC;
const COST_MEMA_W2: f64 = 80_f64 / AREA_PER_SEC;
const COST_USUAL: f64 = 8_f64 / AREA_PER_SEC;
const COST_STEP: f64 = 50_f64 / AREA_PER_SEC;

#[derive(Default, Debug, Clone)]
struct MemoryOperations {
mread_a: u64, // Aligned
mwrite_a: u64,
mread_na1: u64, // Not aligned
mwrite_na1: u64,
mread_na2: u64, // Not aligned
mwrite_na2: u64,
}

#[derive(Debug, Clone)]
pub struct Stats {
mops: MemoryOperations,
usual: u64,
steps: u64,
ops: [u64; 256],
}

/// Default constructor for Stats structure
impl Default for Stats {
fn default() -> Self {
Self { mops: MemoryOperations::default(), usual: 0, steps: 0, ops: [0; 256] }
}
}

impl Stats {
pub fn on_memory_read(&mut self, address: u64, width: u64) {
if (address % 8) != 0 {
if ((address + width) / 8) < (address / 8) {
self.mops.mread_na2 += 1;
} else {
self.mops.mread_na1 += 1;
}
} else {
self.mops.mread_a += 1;
}
}

pub fn on_memory_write(&mut self, address: u64, width: u64) {
if (address % 8) != 0 {
if ((address + width) / 8) < (address / 8) {
self.mops.mwrite_na2 += 1;
} else {
self.mops.mwrite_na1 += 1;
}
} else {
self.mops.mwrite_a += 1;
}
}

pub fn on_steps(&mut self, steps: u64) {
self.steps = steps;
}

pub fn on_op(&mut self, instruction: &ZiskInst, a: u64, b: u64) {
if self.is_usual(instruction, a, b) {
self.usual += 1;
} else {
self.ops[instruction.op as usize] += 1;
}
}

fn is_usual(&self, instruction: &ZiskInst, a: u64, b: u64) -> bool {
instruction.is_external_op && (a < 256) && (b < 256)
}

pub fn report(&self) -> String {
const AREA_PER_SEC: f64 = 1000000_f64;
// Result of his function
let mut output = String::new();

output += "Cost definitions:\n";
output += &format!(" AREA_PER_SEC: {} steps\n", AREA_PER_SEC);
output += &format!(" COST_MEMA_R1: {:02} sec\n", COST_MEMA_R1);
output += &format!(" COST_MEMA_R2: {:02} sec\n", COST_MEMA_R2);
output += &format!(" COST_MEMA_W1: {:02} sec\n", COST_MEMA_W1);
output += &format!(" COST_MEMA_W2: {:02} sec\n", COST_MEMA_W2);
output += &format!(" COST_USUAL: {:02} sec\n", COST_USUAL);
output += &format!(" COST_STEP: {:02} sec\n", COST_STEP);

let total_mem_ops = self.mops.mread_na1 +
self.mops.mread_na2 +
self.mops.mread_a +
self.mops.mwrite_na1 +
self.mops.mwrite_na2 +
self.mops.mwrite_a;
let total_mem_align_steps = self.mops.mread_na1 +
self.mops.mread_na2 * 2 +
self.mops.mwrite_na1 * 2 +
self.mops.mwrite_na2 * 4;

let cost_mem = total_mem_ops as f64 * COST_MEM;
let cost_mem_align = self.mops.mread_na1 as f64 * COST_MEMA_R1 +
self.mops.mread_na2 as f64 * COST_MEMA_R2 +
self.mops.mwrite_na1 as f64 * COST_MEMA_W1 +
self.mops.mwrite_na2 as f64 * COST_MEMA_W2;

let operations = ZiskOperations::new();
let mut total_opcodes: u64 = 0;
let mut opcode_steps: [u64; 256] = [0; 256];
let mut total_opcode_steps: u64 = 0;
let mut opcode_cost: [f64; 256] = [0_f64; 256];
let mut total_opcode_cost: f64 = 0_f64;
for opcode in 0..256 {
// Skip zero counters
if self.ops[opcode] == 0 {
continue;
}

// Increase total
total_opcodes += self.ops[opcode];

// Get the Zisk instruction corresponding to this opcode
let op8 = opcode as u8;
let inst =
operations.op_from_code.get(&op8).expect("Opcode not found in ZiskOperations");

// Increase steps
opcode_steps[opcode] += inst.s;
total_opcode_steps += inst.s;

// Increse cost
let value = self.ops[opcode] as f64;
opcode_cost[opcode] += value * inst.s as f64 / AREA_PER_SEC;
total_opcode_cost += value * inst.s as f64 / AREA_PER_SEC;
}

let cost_usual = self.usual as f64 * COST_USUAL;
let cost_main = self.steps as f64 * COST_STEP;

let total_cost = cost_main + cost_mem + cost_mem_align + total_opcode_cost + cost_usual;

output += &format!("\nTotal Cost: {:.2} sec\n", total_cost);
output += &format!(" Main Cost: {:.2} sec {} steps\n", cost_main, self.steps);
output += &format!(" Mem Cost: {:.2} sec {} steps\n", cost_mem, total_mem_ops);
output +=
&format!(" Mem Align: {:.2} sec {} steps\n", cost_mem_align, total_mem_align_steps);
output += &format!(
" Opcodes: {:.2} sec {} steps ({} ops)\n",
total_opcode_cost, total_opcode_steps, total_opcodes
);
output += &format!(" Usual: {:.2} sec {} steps\n", cost_usual, self.usual);

output += "\nOpcodes:\n";

for opcode in 0..256 {
// Skip zero counters
if self.ops[opcode] == 0 {
continue;
}

// Get the Zisk instruction corresponding to this opcode
let op8 = opcode as u8;
let inst =
operations.op_from_code.get(&op8).expect("Opcode not found in ZiskOperations");

// Log opcode cost
output += &format!(
" {}: {:.2} sec ({} steps/op) ({} ops)\n",
inst.n, opcode_cost[opcode], opcode_steps[opcode], self.ops[opcode]
);
}

output
}
}

0 comments on commit c3d6df4

Please sign in to comment.