wrote instruction logic, just need a new assembler now :)
This commit is contained in:
+139
-165
@@ -4,21 +4,16 @@ use criterion::{
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
use dsa::{FastStore, MainStore, RandomAccessMemory};
|
||||
use dsa::RandomAccessMemory;
|
||||
|
||||
// ── reproduce the trait and PhysAddr here, or import from your crate ─────────
|
||||
type PhysAddr = u32;
|
||||
|
||||
// ── address generators ────────────────────────────────────────────────────────
|
||||
|
||||
/// Cycles through a small set of addresses — stays hot in L1/L2.
|
||||
/// Simulates a tight inner loop hitting the same working set repeatedly.
|
||||
fn sequential_addrs(n: usize, max_addr: u32) -> Vec<PhysAddr> {
|
||||
(0..n).map(|i| ((i as u32 * 4) & (max_addr - 1))).collect()
|
||||
}
|
||||
|
||||
/// LCG pseudo-random addresses across the full address space.
|
||||
/// Simulates worst-case cache behaviour.
|
||||
fn random_addrs(n: usize, max_addr: u32) -> Vec<PhysAddr> {
|
||||
let mut addrs = Vec::with_capacity(n);
|
||||
let mut x: u32 = 0xdeadbeef;
|
||||
@@ -29,205 +24,184 @@ fn random_addrs(n: usize, max_addr: u32) -> Vec<PhysAddr> {
|
||||
addrs
|
||||
}
|
||||
|
||||
/// Walks addresses page by page — tests page-boundary crossing behaviour.
|
||||
fn page_stride_addrs(n: usize) -> Vec<PhysAddr> {
|
||||
(0..n).map(|i| (i as u32 * 4096) & 0x00FF_FFFF).collect()
|
||||
}
|
||||
|
||||
// ── generic benchmark functions ───────────────────────────────────────────────
|
||||
|
||||
fn bench_read_byte<M: RandomAccessMemory>(
|
||||
group: &mut BenchmarkGroup<WallTime>,
|
||||
name: &str,
|
||||
mem: &M,
|
||||
addrs: &[PhysAddr],
|
||||
) {
|
||||
group.throughput(Throughput::Elements(addrs.len() as u64));
|
||||
group.bench_function(name, |b| {
|
||||
b.iter(|| {
|
||||
let mut sum: u32 = 0;
|
||||
for &addr in addrs {
|
||||
sum = sum.wrapping_add(mem.read_byte(black_box(addr)) as u32);
|
||||
}
|
||||
black_box(sum)
|
||||
})
|
||||
});
|
||||
fn random_page_addrs(n: usize, max_addr: u32) -> Vec<PhysAddr> {
|
||||
let mut addrs = Vec::with_capacity(n);
|
||||
let mut x: u32 = 0xc0ffee42;
|
||||
for _ in 0..n {
|
||||
x = x.wrapping_mul(1664525).wrapping_add(1013904223);
|
||||
addrs.push((x & (max_addr - 1)) & !0xFFF);
|
||||
}
|
||||
addrs
|
||||
}
|
||||
|
||||
fn bench_write_byte<M: RandomAccessMemory>(
|
||||
// ── runners ───────────────────────────────────────────────────────────────────
|
||||
|
||||
fn run_read_byte(
|
||||
group: &mut BenchmarkGroup<WallTime>,
|
||||
name: &str,
|
||||
mem: &mut M,
|
||||
addrs: &[PhysAddr],
|
||||
implementations: &mut [(&str, Box<dyn RandomAccessMemory>)],
|
||||
) {
|
||||
group.throughput(Throughput::Elements(addrs.len() as u64));
|
||||
group.bench_function(name, |b| {
|
||||
b.iter(|| {
|
||||
for (i, &addr) in addrs.iter().enumerate() {
|
||||
mem.write_byte(black_box(addr), black_box(i as u8));
|
||||
}
|
||||
})
|
||||
});
|
||||
for (name, mem) in implementations.iter() {
|
||||
group.bench_with_input(BenchmarkId::new(*name, ""), addrs, |b, addrs| {
|
||||
b.iter(|| {
|
||||
let mut sum: u32 = 0;
|
||||
for &addr in addrs {
|
||||
sum = sum.wrapping_add(mem.read_byte(black_box(addr)) as u32);
|
||||
}
|
||||
black_box(sum)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_read_word<M: RandomAccessMemory>(
|
||||
fn run_write_byte(
|
||||
group: &mut BenchmarkGroup<WallTime>,
|
||||
name: &str,
|
||||
mem: &M,
|
||||
addrs: &[PhysAddr],
|
||||
implementations: &mut [(&str, Box<dyn RandomAccessMemory>)],
|
||||
) {
|
||||
group.throughput(Throughput::Elements(addrs.len() as u64));
|
||||
group.bench_function(name, |b| {
|
||||
b.iter(|| {
|
||||
let mut sum: u32 = 0;
|
||||
for &addr in addrs {
|
||||
sum = sum.wrapping_add(mem.read_word(black_box(addr)));
|
||||
}
|
||||
black_box(sum)
|
||||
})
|
||||
});
|
||||
for (name, mem) in implementations.iter_mut() {
|
||||
group.bench_with_input(BenchmarkId::new(*name, ""), addrs, |b, addrs| {
|
||||
b.iter(|| {
|
||||
for (i, &addr) in addrs.iter().enumerate() {
|
||||
mem.write_byte(black_box(addr), black_box(i as u8));
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_write_word<M: RandomAccessMemory>(
|
||||
fn run_read_word(
|
||||
group: &mut BenchmarkGroup<WallTime>,
|
||||
name: &str,
|
||||
mem: &mut M,
|
||||
addrs: &[PhysAddr],
|
||||
implementations: &mut [(&str, Box<dyn RandomAccessMemory>)],
|
||||
) {
|
||||
group.throughput(Throughput::Elements(addrs.len() as u64));
|
||||
group.bench_function(name, |b| {
|
||||
b.iter(|| {
|
||||
for (i, &addr) in addrs.iter().enumerate() {
|
||||
mem.write_word(black_box(addr), black_box(i as u32));
|
||||
}
|
||||
})
|
||||
});
|
||||
for (name, mem) in implementations.iter() {
|
||||
group.bench_with_input(BenchmarkId::new(*name, ""), addrs, |b, addrs| {
|
||||
b.iter(|| {
|
||||
let mut sum: u32 = 0;
|
||||
for &addr in addrs {
|
||||
sum = sum.wrapping_add(mem.read_word(black_box(addr)));
|
||||
}
|
||||
black_box(sum)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_read_page<M: RandomAccessMemory>(
|
||||
fn run_write_word(
|
||||
group: &mut BenchmarkGroup<WallTime>,
|
||||
name: &str,
|
||||
mem: &M,
|
||||
addrs: &[PhysAddr],
|
||||
implementations: &mut [(&str, Box<dyn RandomAccessMemory>)],
|
||||
) {
|
||||
group.throughput(Throughput::Elements(addrs.len() as u64));
|
||||
for (name, mem) in implementations.iter_mut() {
|
||||
group.bench_with_input(BenchmarkId::new(*name, ""), addrs, |b, addrs| {
|
||||
b.iter(|| {
|
||||
for (i, &addr) in addrs.iter().enumerate() {
|
||||
mem.write_word(black_box(addr), black_box(i as u32));
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn run_read_page(
|
||||
group: &mut BenchmarkGroup<WallTime>,
|
||||
addrs: &[PhysAddr],
|
||||
implementations: &mut [(&str, Box<dyn RandomAccessMemory>)],
|
||||
) {
|
||||
group.throughput(Throughput::Bytes(addrs.len() as u64 * 4096));
|
||||
group.bench_function(name, |b| {
|
||||
b.iter(|| {
|
||||
let mut sum: u8 = 0;
|
||||
for &addr in addrs {
|
||||
let page = mem.read_page(black_box(addr));
|
||||
sum = sum.wrapping_add(page[0]);
|
||||
}
|
||||
black_box(sum)
|
||||
})
|
||||
});
|
||||
for (name, mem) in implementations.iter() {
|
||||
group.bench_with_input(BenchmarkId::new(*name, ""), addrs, |b, addrs| {
|
||||
b.iter(|| {
|
||||
let mut sum: u64 = 0;
|
||||
for &addr in addrs {
|
||||
let page = mem.read_page(black_box(addr));
|
||||
for chunk in page.chunks_exact(8) {
|
||||
sum = sum.wrapping_add(u64::from_le_bytes(chunk.try_into().unwrap()));
|
||||
}
|
||||
}
|
||||
black_box(sum)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_write_page<M: RandomAccessMemory>(
|
||||
fn run_write_page(
|
||||
group: &mut BenchmarkGroup<WallTime>,
|
||||
name: &str,
|
||||
mem: &mut M,
|
||||
addrs: &[PhysAddr],
|
||||
implementations: &mut [(&str, Box<dyn RandomAccessMemory>)],
|
||||
) {
|
||||
let page_data = [0xABu8; 4096];
|
||||
group.throughput(Throughput::Bytes(addrs.len() as u64 * 4096));
|
||||
group.bench_function(name, |b| {
|
||||
b.iter(|| {
|
||||
for &addr in addrs {
|
||||
mem.write_page(black_box(addr), black_box(&page_data));
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// ── run all access patterns for a given implementation ────────────────────────
|
||||
|
||||
fn bench_implementation<M: RandomAccessMemory>(
|
||||
c: &mut Criterion,
|
||||
impl_name: &str,
|
||||
mut mem: M,
|
||||
max_addr: u32,
|
||||
) {
|
||||
const N: usize = 64;
|
||||
|
||||
let seq_addrs = sequential_addrs(N, max_addr);
|
||||
let rand_addrs = random_addrs(N, max_addr);
|
||||
let page_stride = page_stride_addrs(N);
|
||||
|
||||
// ── read_byte ────────────────────────────────────────────────────────────
|
||||
{
|
||||
let mut g = c.benchmark_group(format!("{}/read_byte", impl_name));
|
||||
g.measurement_time(Duration::from_secs(3));
|
||||
bench_read_byte(&mut g, "sequential", &mem, &seq_addrs);
|
||||
bench_read_byte(&mut g, "random", &mem, &rand_addrs);
|
||||
g.finish();
|
||||
}
|
||||
|
||||
// ── write_byte ───────────────────────────────────────────────────────────
|
||||
{
|
||||
let mut g = c.benchmark_group(format!("{}/write_byte", impl_name));
|
||||
g.measurement_time(Duration::from_secs(3));
|
||||
bench_write_byte(&mut g, "sequential", &mut mem, &seq_addrs);
|
||||
bench_write_byte(&mut g, "random", &mut mem, &rand_addrs);
|
||||
g.finish();
|
||||
}
|
||||
|
||||
// ── read_word ────────────────────────────────────────────────────────────
|
||||
{
|
||||
let mut g = c.benchmark_group(format!("{}/read_word", impl_name));
|
||||
g.measurement_time(Duration::from_secs(3));
|
||||
bench_read_word(&mut g, "sequential", &mem, &seq_addrs);
|
||||
bench_read_word(&mut g, "random", &mem, &rand_addrs);
|
||||
g.finish();
|
||||
}
|
||||
|
||||
// ── write_word ───────────────────────────────────────────────────────────
|
||||
{
|
||||
let mut g = c.benchmark_group(format!("{}/write_word", impl_name));
|
||||
g.measurement_time(Duration::from_secs(3));
|
||||
bench_write_word(&mut g, "sequential", &mut mem, &seq_addrs);
|
||||
bench_write_word(&mut g, "random", &mut mem, &rand_addrs);
|
||||
g.finish();
|
||||
}
|
||||
|
||||
// ── read_page ────────────────────────────────────────────────────────────
|
||||
{
|
||||
let mut g = c.benchmark_group(format!("{}/read_page", impl_name));
|
||||
g.measurement_time(Duration::from_secs(3));
|
||||
bench_read_page(&mut g, "sequential", &mem, &seq_addrs);
|
||||
bench_read_page(&mut g, "random", &mem, &rand_addrs);
|
||||
bench_read_page(&mut g, "page_stride", &mem, &page_stride);
|
||||
g.finish();
|
||||
}
|
||||
|
||||
// ── write_page ───────────────────────────────────────────────────────────
|
||||
{
|
||||
let mut g = c.benchmark_group(format!("{}/write_page", impl_name));
|
||||
g.measurement_time(Duration::from_secs(3));
|
||||
bench_write_page(&mut g, "sequential", &mut mem, &seq_addrs);
|
||||
bench_write_page(&mut g, "random", &mut mem, &rand_addrs);
|
||||
bench_write_page(&mut g, "page_stride", &mut mem, &page_stride);
|
||||
g.finish();
|
||||
for (name, mem) in implementations.iter_mut() {
|
||||
group.bench_with_input(BenchmarkId::new(*name, ""), addrs, |b, addrs| {
|
||||
b.iter(|| {
|
||||
for &addr in addrs {
|
||||
mem.write_page(black_box(addr), black_box(&page_data));
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── wire up your implementations here ────────────────────────────────────────
|
||||
//
|
||||
// Replace these stubs with your actual types. Each call to bench_implementation
|
||||
// runs the full suite and labels it separately in the HTML report.
|
||||
//
|
||||
// Example:
|
||||
// fn benchmarks(c: &mut Criterion) {
|
||||
// bench_implementation(c, "MainStore", MainStore::new(), 0x00FF_FFFF);
|
||||
// bench_implementation(c, "FxHashMap", HashMapMem::new(), 0x00FF_FFFF);
|
||||
// }
|
||||
//
|
||||
// ── entry point ───────────────────────────────────────────────────────────────
|
||||
|
||||
fn benchmarks(c: &mut Criterion) {
|
||||
// TODO: replace with your implementations
|
||||
bench_implementation(c, "MainStore", MainStore::new(), 0x00FF_FFFF);
|
||||
bench_implementation(c, "FastStore", FastStore::new(), 0x00FF_FFFF);
|
||||
const N: usize = 4096;
|
||||
const MAX_ADDR: u32 = 0x00FF_FFFF;
|
||||
|
||||
let _ = c;
|
||||
let seq_addrs = sequential_addrs(N, MAX_ADDR);
|
||||
let rand_addrs = random_addrs(N, MAX_ADDR);
|
||||
let page_addrs_seq = page_stride_addrs(N);
|
||||
let page_addrs_rand = random_page_addrs(N, MAX_ADDR);
|
||||
|
||||
let mut implementations: Vec<(&str, Box<dyn RandomAccessMemory>)> = vec![
|
||||
#[cfg(feature = "mainstore-hashmap")]
|
||||
("HashStore", Box::new(dsa::HashStore::new())),
|
||||
#[cfg(feature = "mainstore-arraymap")]
|
||||
("ArrayStore", Box::new(dsa::ArrayStore::new())),
|
||||
#[cfg(feature = "mainstore-stackarray")]
|
||||
("StackArrayStore", Box::new(dsa::StackArrayStore::new())),
|
||||
#[cfg(feature = "mainstore-prealloc")]
|
||||
("PreAllocStore", Box::new(dsa::PreAllocStore::new())),
|
||||
#[cfg(feature = "mainstore-bulkalloc")]
|
||||
("BulkAllocStore", Box::new(dsa::BulkAllocStore::new())),
|
||||
];
|
||||
|
||||
macro_rules! group {
|
||||
($name:expr, $secs:expr, $runner:ident, $addrs:expr) => {{
|
||||
let mut g = c.benchmark_group($name);
|
||||
g.measurement_time(Duration::from_secs($secs));
|
||||
$runner(&mut g, $addrs, &mut implementations);
|
||||
g.finish();
|
||||
}};
|
||||
}
|
||||
|
||||
group!("write_word/random", 30, run_write_word, &rand_addrs);
|
||||
group!("write_byte/random", 30, run_write_byte, &rand_addrs);
|
||||
|
||||
group!("read_byte/sequential", 30, run_read_byte, &seq_addrs);
|
||||
group!("read_byte/random", 30, run_read_byte, &rand_addrs);
|
||||
|
||||
group!("read_word/sequential", 30, run_read_word, &seq_addrs);
|
||||
group!("read_word/random", 30, run_read_word, &rand_addrs);
|
||||
|
||||
group!("write_byte/sequential", 30, run_write_byte, &seq_addrs);
|
||||
group!("write_word/sequential", 30, run_write_word, &seq_addrs);
|
||||
|
||||
group!("read_page/sequential", 30, run_read_page, &page_addrs_seq);
|
||||
group!("read_page/random", 30, run_read_page, &page_addrs_rand);
|
||||
|
||||
group!("write_page/sequential", 30, run_write_page, &page_addrs_seq);
|
||||
group!("write_page/random", 30, run_write_page, &page_addrs_rand);
|
||||
}
|
||||
|
||||
criterion_group!(benches, benchmarks);
|
||||
|
||||
Reference in New Issue
Block a user