use framealloc::SmartAlloc;
use std::time::Instant;
#[derive(Debug, Clone, Copy)]
struct Particle {
position: [f32; 3],
velocity: [f32; 3],
color: [u8; 4],
life: f32,
}
impl Particle {
fn new() -> Self {
Self {
position: [0.0; 3],
velocity: [
(rand::random::<f32>() - 0.5) * 10.0,
(rand::random::<f32>() - 0.5) * 10.0,
(rand::random::<f32>() - 0.5) * 10.0,
],
color: [255; 4],
life: 1.0,
}
}
fn update(&mut self, dt: f32) {
self.position[0] += self.velocity[0] * dt;
self.position[1] += self.velocity[1] * dt;
self.position[2] += self.velocity[2] * dt;
self.velocity[1] -= 9.81 * dt; self.life -= dt;
}
}
fn individual_allocation(count: usize) -> Vec<Particle> {
let alloc = SmartAlloc::new(Default::default());
alloc.begin_frame();
let mut particles = Vec::with_capacity(count);
for _ in 0..count {
let particle = alloc.frame_alloc::<Particle>();
particles.push(*particle);
}
alloc.end_frame();
particles
}
fn batch_allocation(count: usize) -> Vec<Particle> {
let alloc = SmartAlloc::new(Default::default());
alloc.begin_frame();
let batch = unsafe {
let batch = alloc.frame_alloc_batch::<Particle>(count);
for i in 0..count {
let particle = batch.add(i);
std::ptr::write(particle, Particle::new());
}
batch
};
let mut particles = Vec::with_capacity(count);
for i in 0..count {
particles.push(unsafe { *batch.get(i) });
}
alloc.end_frame();
particles
}
fn specialized_batch_sizes() {
let alloc = SmartAlloc::new(Default::default());
alloc.begin_frame();
let [p1, p2] = alloc.frame_alloc_2::<Particle>();
let [p3, p4, p5, p6] = alloc.frame_alloc_4::<Particle>();
let batch8 = alloc.frame_alloc_8::<Particle>();
*p1 = Particle::new();
*p2 = Particle::new();
*p3 = Particle::new();
*p4 = Particle::new();
*p5 = Particle::new();
*p6 = Particle::new();
for i in 0..8 {
batch8[i] = Particle::new();
}
println!("Specialized batches:");
println!(" 2 items: {:p}, {:p}", p1, p2);
println!(" 4 items: {:p}, {:p}, {:p}, {:p}", p3, p4, p5, p6);
println!(" 8 items: {:p}..{:p}", &batch8[0], &batch8[7]);
alloc.end_frame();
}
fn particle_system_simulation() {
let alloc = SmartAlloc::new(Default::default());
const PARTICLE_COUNT: usize = 100_000;
const FRAME_COUNT: usize = 60;
println!("\n=== Particle System Simulation ===");
println!("Simulating {} particles for {} frames", PARTICLE_COUNT, FRAME_COUNT);
let mut total_time = Duration::new(0, 0);
for frame in 0..FRAME_COUNT {
let frame_start = Instant::now();
alloc.begin_frame();
let particles = unsafe {
let batch = alloc.frame_alloc_batch::<Particle>(PARTICLE_COUNT);
for i in 0..PARTICLE_COUNT {
let particle = batch.add(i);
if frame == 0 {
std::ptr::write(particle, Particle::new());
}
}
batch
};
let dt = 1.0 / 60.0;
for i in 0..PARTICLE_COUNT {
let particle = unsafe { &mut *particles.get_mut(i) };
particle.update(dt);
}
let mut alive_count = 0;
for i in 0..PARTICLE_COUNT {
let particle = unsafe { particles.get(i) };
if particle.life > 0.0 {
alive_count += 1;
}
}
alloc.end_frame();
let frame_time = frame_start.elapsed();
total_time += frame_time;
if frame % 10 == 0 {
println!("Frame {}: {} alive, {:?}", frame, alive_count, frame_time);
}
}
let avg_frame_time = total_time / FRAME_COUNT as u32;
println!("Average frame time: {:?}", avg_frame_time);
println!("Particles per second: {:.0}",
PARTICLE_COUNT as f64 / avg_frame_time.as_secs_f64());
}
fn benchmark_comparison() {
const COUNTS: &[usize] = &[100, 1000, 10000, 100000];
println!("\n=== Performance Comparison ===");
println!("Testing individual vs batch allocation:");
for &count in COUNTS {
let start = Instant::now();
let _individual = individual_allocation(count);
let individual_time = start.elapsed();
let start = Instant::now();
let _batch = batch_allocation(count);
let batch_time = start.elapsed();
let speedup = individual_time.as_nanos() as f64 / batch_time.as_nanos() as f64;
println!(" {}: individual={:?}, batch={:?}, {:.1}x speedup",
count, individual_time, batch_time, speedup);
}
}
fn memory_layout_optimization() {
println!("\n=== Memory Layout Optimization ===");
let alloc = SmartAlloc::new(Default::default());
alloc.begin_frame();
struct ParticleSystemSoA {
positions: FrameBox<[[f32; 3]>>,
velocities: FrameBox<[[f32; 3]>>,
colors: FrameBox<[[u8; 4]>>,
lives: FrameBox<[f32]>,
}
impl ParticleSystemSoA {
fn new(count: usize, alloc: &SmartAlloc) -> Self {
Self {
positions: alloc.frame_box([[0.0; 3]; count]),
velocities: alloc.frame_box([[0.0; 3]; count]),
colors: alloc.frame_box([[255; 4]; count]),
lives: alloc.frame_box([0.0; count]),
}
}
fn update(&mut self, dt: f32) {
for i in 0..self.lives.len() {
self.positions[i][0] += self.velocities[i][0] * dt;
self.positions[i][1] += self.velocities[i][1] * dt;
self.positions[i][2] += self.velocities[i][2] * dt;
self.velocities[i][1] -= 9.81 * dt;
self.lives[i] -= dt;
}
}
}
let mut soa_system = ParticleSystemSoA::new(10000, &alloc);
for frame in 0..60 {
soa_system.update(1.0 / 60.0);
if frame % 20 == 0 {
let alive = soa_system.lives.iter().filter(|&&l| l > 0.0).count();
println!(" Frame {}: {} particles alive", frame, alive);
}
}
alloc.end_frame();
}
fn zero_copy_buffer_sharing() {
println!("\n=== Zero-Copy Buffer Sharing ===");
let alloc = SmartAlloc::new(Default::default());
alloc.begin_frame();
let buffer = alloc.frame_slice::<f32>(10000);
for i in 0..10000 {
buffer[i] = i as f32;
}
struct BufferView<'a> {
data: &'a [f32],
offset: usize,
len: usize,
}
impl<'a> BufferView<'a> {
fn new(buffer: &'a [f32], offset: usize, len: usize) -> Self {
Self {
data: &buffer[offset..offset + len],
offset,
len,
}
}
fn sum(&self) -> f64 {
self.data.iter().map(|&x| x as f64).sum()
}
}
let view1 = BufferView::new(buffer, 0, 2500);
let view2 = BufferView::new(buffer, 2500, 2500);
let view3 = BufferView::new(buffer, 5000, 2500);
let view4 = BufferView::new(buffer, 7500, 2500);
println!(" Buffer views (zero-copy):");
println!(" View 1 sum: {:.0}", view1.sum());
println!(" View 2 sum: {:.0}", view2.sum());
println!(" View 3 sum: {:.0}", view3.sum());
println!(" View 4 sum: {:.0}", view4.sum());
alloc.end_frame();
}
fn main() {
println!("=== Batch Optimization Demo ===\n");
specialized_batch_sizes();
benchmark_comparison();
particle_system_simulation();
memory_layout_optimization();
zero_copy_buffer_sharing();
println!("\nAll batch optimization demos completed!");
println!("\n=== Performance Tips ===");
println!("1. Use batch allocation for >100 items");
println!("2. Use specialized sizes for known small counts");
println!("3. Structure of Arrays improves SIMD performance");
println!("4. Zero-copy views reduce memory bandwidth");
println!("5. Always profile with realistic data sizes");
}
use std::time::Duration;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_batch_vs_individual() {
const COUNT: usize = 1000;
let individual = individual_allocation(COUNT);
let batch = batch_allocation(COUNT);
assert_eq!(individual.len(), COUNT);
assert_eq!(batch.len(), COUNT);
}
#[test]
fn test_specialized_sizes() {
let alloc = SmartAlloc::new(Default::default());
alloc.begin_frame();
let [a, b] = alloc.frame_alloc_2::<u32>();
let [c, d, e, f] = alloc.frame_alloc_4::<u32>();
*a = 1;
*b = 2;
*c = 3;
*d = 4;
*e = 5;
*f = 6;
assert_eq!(*a, 1);
assert_eq!(*b, 2);
assert_eq!(*c, 3);
assert_eq!(*d, 4);
assert_eq!(*e, 5);
assert_eq!(*f, 6);
alloc.end_frame();
}
}