Skip to content

Commit

Permalink
Automated benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoRiether committed May 9, 2024
1 parent c2d5e7f commit fae65f1
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 165 deletions.
272 changes: 140 additions & 132 deletions Cargo.lock

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ authors = ["Leonardo Riether <[email protected]>"]
edition = "2021"
description = "Fast Pretty Good RISC-V Assembly Rendering System"
license = "MIT"
exclude = ["site/", "tests/riscv-tests/", "samples/"]
exclude = ["site/", "tests/riscv-tests/"]
repository = "https://github.com/LeoRiether/FPGRARS/"

[lib]
bench = false

[[bin]]
name = "fpgrars"
path = "src/main.rs"
bench = false

[features]
show_ms = [] # show ms per frame in the window title
Expand Down Expand Up @@ -43,10 +50,16 @@ thiserror = "1.0.40"
owo-colors = "3.5.0"
serde = { version = "1.0.171", features = ["derive"] }
toml = "0.7.6"
criterion = "0.5.1"
ahash = "0.8.11"

[dependencies.clap]
version = "4.3"
version = "4.5"
features = ["suggestions", "color", "wrap_help", "cargo", "derive"]

[dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] }

[[bench]]
name = "simulator"
harness = false

File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 6 additions & 6 deletions samples/bench/sort.s → benches/samples/sort.s
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#############################################################

.data
N: .word 10000
N: .word 5000

.text
# Allocates the array in the stack with N integers
Expand All @@ -27,7 +27,7 @@ N: .word 10000
mv a1 s0
jal check_sorted
li a7 1
ecall
# ecall

# Save time
# csrr s7 time
Expand All @@ -46,18 +46,18 @@ N: .word 10000
mv a1 s0
jal check_sorted
li a7 1
ecall
# ecall

# Print time elapsed
li a0 '\n'
li a7 11
ecall
# ecall
mv a0 s7
li a7 1
ecall
# ecall
li a0 '\n'
li a7 11
ecall
# ecall

exit:
li a7 10
Expand Down
6 changes: 3 additions & 3 deletions samples/bench/video.s → benches/samples/video.s
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#######################################################

.data
times: .word 1000
times: .word 300
color: .byte 0xf8 0x07

.align 1
Expand Down Expand Up @@ -45,10 +45,10 @@ exit:
csrr a0 time
sub a0 a0 s2
li a7 1
ecall
# ecall
li a0 '\n'
li a7 11
ecall
# ecall

li a7 10
ecall
Expand Down
21 changes: 21 additions & 0 deletions benches/simulator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use criterion::{criterion_group, criterion_main, Criterion};
use fpgrars::simulator::{self, Simulator};

const TESTCASES: &[&str] = &["add.s", "memory.s", "memory2.s", "sort.s", "video.s"];

fn criterion_benchmark(c: &mut Criterion) {
for testcase in TESTCASES {
c.bench_function(testcase, |b| {
let memory = simulator::memory::Memory::new();
let mut simulator = Simulator::default().with_memory(memory);
simulator
.load_file(&format!("./benches/samples/{testcase}"))
.unwrap_or_else(|e| panic!("Couldn't parse {testcase}: {e}"));

b.iter(|| simulator.run())
});
}
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
22 changes: 14 additions & 8 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use clap::Parser;
use lazy_static::lazy_static;
use serde::Deserialize;

lazy_static! {
pub static ref CONFIG: Config = OptionalConfig::get_toml()
.merge(OptionalConfig::get_args())
.into();
}

#[derive(Parser, Deserialize, Debug, Default)]
#[command(author, version, about)]
#[clap(disable_help_flag = true)]
pub struct OptionalConfig {
#[clap(long, action = clap::ArgAction::HelpLong)]
help: Option<bool>,

/// Hides the bitmap display
#[arg(long)]
pub no_video: bool,
Expand Down Expand Up @@ -57,6 +54,7 @@ impl OptionalConfig {

pub fn merge(self, rhs: Self) -> Self {
Self {
help: self.help.or(rhs.help),
no_video: self.no_video || rhs.no_video,
width: self.width.or(rhs.width),
height: self.height.or(rhs.height),
Expand All @@ -69,7 +67,7 @@ impl OptionalConfig {
}
}

#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Config {
pub no_video: bool,
pub width: usize,
Expand Down Expand Up @@ -98,3 +96,11 @@ impl From<OptionalConfig> for Config {
}
}
}

impl Config {
pub fn get() -> Self {
OptionalConfig::get_toml()
.merge(OptionalConfig::get_args())
.into()
}
}
11 changes: 6 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use owo_colors::OwoColorize;
use fpgrars::simulator::Simulator;
use std::error::Error;
use std::thread;
use fpgrars::config::CONFIG;

fn main() -> Result<(), Box<dyn Error>> {
let config = fpgrars::config::Config::get();

let memory = fpgrars::simulator::memory::Memory::new();
let mmio = memory.mmio.clone();

Expand All @@ -13,9 +14,9 @@ fn main() -> Result<(), Box<dyn Error>> {
.spawn(move || {
let mut sim = Simulator::default()
.with_memory(memory)
.with_midi_port(CONFIG.port);
.with_midi_port(config.port);

if let Err(e) = sim.load_file(&CONFIG.file) {
if let Err(e) = sim.load_file(&config.file) {
eprintln!(" {}: {}\n", "[error]".bright_red().bold(), e);
std::process::exit(1);
};
Expand All @@ -26,8 +27,8 @@ fn main() -> Result<(), Box<dyn Error>> {
std::process::exit(exit_code);
})?;

if !CONFIG.no_video {
let state = fpgrars::renderer::State::new(mmio, CONFIG.width, CONFIG.height, CONFIG.scale);
if !config.no_video {
let state = fpgrars::renderer::State::new(mmio, config.width, config.height, config.scale);
fpgrars::renderer::init(state);
}

Expand Down
1 change: 0 additions & 1 deletion src/simulator/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ fn from_bool(x: bool) -> u32 {
/// affected. However, if `op: fn(u32, u32) -> R`, performance is significantly worse! Like,
/// 50%-60% worse. This might be because generics are monomorphized, and each lambda is a different
/// Fn type.
/// TODO: Automatic benchmarks.
#[inline(always)]
fn exec_type_r<R, F>(rd: u8, rs1: u8, rs2: u8, op: F) -> Executor
where
Expand Down
42 changes: 35 additions & 7 deletions src/simulator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ pub mod memory;
mod midi;
mod util;

use crate::{config, parser};
use crate::config::Config;
use crate::instruction::Instruction;
use crate::renderer::{FRAME_0, FRAME_1, FRAME_SIZE};
use crate::parser;
use into_register::*;
use memory::*;
use owo_colors::OwoColorize;
use std::hint::black_box;
use std::{mem, time};

/// Returned by the [ecall](struct.Simulator.html#method.ecall) procedure
Expand All @@ -29,6 +32,8 @@ enum EcallSignal {
/// Simulates a RISC-V CPU. Generally initialized by calling [load_from_file](struct.Simulator.html#method.load_from_file)
/// and ran by calling [run](struct.Simulator.html#method.run).
pub struct Simulator {
config: Config,

registers: [u32; 32],
floats: [f32; 32],
status: Vec<u32>, // I'm not sure myself how many status registers I'll use
Expand All @@ -41,12 +46,13 @@ pub struct Simulator {

pub memory: Memory,
pub code: Vec<executor::Executor>,
pub code_ctx: Vec<crate::parser::token::Context>,
pub code_ctx: Vec<parser::token::Context>,
}

impl Default for Simulator {
fn default() -> Self {
Self {
config: Config::default(),
registers: [0; 32],
floats: [0.0; 32],
status: Vec::new(),
Expand All @@ -64,12 +70,18 @@ impl Default for Simulator {

impl Simulator {
pub fn load_file(&mut self, path: &str) -> Result<(), parser::error::Error> {
let parsed = parser::parse(path, DATA_SIZE)?;
self.load_parsed_output(parsed);
Ok(())
}

fn load_parsed_output(&mut self, parsed: parser::Parsed) {
let parser::Parsed {
code,
code_ctx,
data,
globl,
} = parser::parse(path, DATA_SIZE)?;
} = parsed;

self.code = executor::compile_all(&code);
self.code_ctx = code_ctx;
Expand All @@ -79,12 +91,20 @@ impl Simulator {
self.pc = globl;
}

if config::CONFIG.print_instructions {
if self.config.print_instructions {
eprintln!("{}", "Instructions: ---------------".bright_blue());
code.iter().for_each(|i| eprintln!("{:?}", i));
eprintln!("{}", "-----------------------------".bright_blue());
}
}

// BUG: this shouldn't be here
pub fn nothing(&mut self) -> Result<(), parser::error::Error> {
self.code = executor::compile_all(black_box(&[
Instruction::Li(17, 10), // li a7 10
Instruction::Li(10, 0), // li a0 0
Instruction::Ecall,
]));
Ok(())
}

Expand All @@ -98,6 +118,11 @@ impl Simulator {
self
}

pub fn with_config(mut self, config: Config) -> Self {
self.config = config;
self
}

#[inline]
fn reg<T: FromRegister>(&self, i: u8) -> T {
FromRegister::from(unsafe { *self.registers.get_unchecked(i as usize) })
Expand All @@ -120,7 +145,7 @@ impl Simulator {

#[allow(clippy::needless_range_loop)]
pub fn print_state(&self) {
use crate::parser::register_names::REGVEC;
use parser::register_names::REGVEC;

eprintln!("{}", "Registers:".bright_blue());
for i in 0..32 {
Expand Down Expand Up @@ -164,21 +189,24 @@ impl Simulator {

pub fn run(&mut self) -> i32 {
self.init();
if self.code.is_empty() {
return 0;
}

// Copy code to local variable so we can access it without borrowing self
let code = mem::take(&mut self.code);

executor::next(self, &code, self.pc);

if config::CONFIG.print_state {
if self.config.print_state {
self.print_state();
}

self.exit_code
}

fn ecall(&mut self) -> EcallSignal {
use crate::parser::register_names::*;
use parser::register_names::*;
use rand::{thread_rng, Rng};

let a7 = self.reg::<u32>(17);
Expand Down

0 comments on commit fae65f1

Please sign in to comment.