code review results
Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
217
crates/lib/benches/write_buffer.rs
Normal file
217
crates/lib/benches/write_buffer.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
//! WriteBuffer performance benchmarks
|
||||
//!
|
||||
//! These benchmarks measure the performance improvements from the O(n²) → O(1)
|
||||
//! optimization that replaced Vec::retain() with HashMap-based indexing.
|
||||
|
||||
use criterion::{
|
||||
black_box,
|
||||
criterion_group,
|
||||
criterion_main,
|
||||
BenchmarkId,
|
||||
Criterion,
|
||||
};
|
||||
use lib::persistence::{
|
||||
PersistenceOp,
|
||||
WriteBuffer,
|
||||
};
|
||||
|
||||
/// Benchmark: Add many updates to the same component (worst case for old implementation)
|
||||
///
|
||||
/// This scenario heavily stresses the deduplication logic. In the old O(n)
|
||||
/// implementation, each add() would scan the entire buffer. With HashMap
|
||||
/// indexing, this is now O(1).
|
||||
fn bench_repeated_updates_same_component(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("WriteBuffer::add (same component)");
|
||||
|
||||
for num_updates in [10, 100, 500, 1000] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(num_updates),
|
||||
&num_updates,
|
||||
|b, &num_updates| {
|
||||
b.iter(|| {
|
||||
let mut buffer = WriteBuffer::new(2000);
|
||||
let entity_id = uuid::Uuid::new_v4();
|
||||
|
||||
// Apply many updates to the same (entity, component) pair
|
||||
for i in 0..num_updates {
|
||||
let op = PersistenceOp::UpsertComponent {
|
||||
entity_id,
|
||||
component_type: "Transform".to_string(),
|
||||
data: vec![i as u8; 100], // 100 bytes per update
|
||||
};
|
||||
buffer.add(op).ok(); // Ignore errors for benchmarking
|
||||
}
|
||||
|
||||
black_box(buffer);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: Add updates to different components (best case, no deduplication)
|
||||
///
|
||||
/// This tests the performance when there's no deduplication happening.
|
||||
/// Both old and new implementations should perform similarly here.
|
||||
fn bench_different_components(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("WriteBuffer::add (different components)");
|
||||
|
||||
for num_components in [10, 100, 500, 1000] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(num_components),
|
||||
&num_components,
|
||||
|b, &num_components| {
|
||||
b.iter(|| {
|
||||
let mut buffer = WriteBuffer::new(2000);
|
||||
let entity_id = uuid::Uuid::new_v4();
|
||||
|
||||
// Add different components (no deduplication)
|
||||
for i in 0..num_components {
|
||||
let op = PersistenceOp::UpsertComponent {
|
||||
entity_id,
|
||||
component_type: format!("Component{}", i),
|
||||
data: vec![i as u8; 100],
|
||||
};
|
||||
buffer.add(op).ok();
|
||||
}
|
||||
|
||||
black_box(buffer);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: Mixed workload (realistic scenario)
|
||||
///
|
||||
/// This simulates a realistic workload with a mix of repeated updates to
|
||||
/// popular components and unique component updates.
|
||||
fn bench_mixed_workload(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("WriteBuffer::add (mixed workload)");
|
||||
|
||||
for num_ops in [100, 500, 1000] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(num_ops),
|
||||
&num_ops,
|
||||
|b, &num_ops| {
|
||||
b.iter(|| {
|
||||
let mut buffer = WriteBuffer::new(2000);
|
||||
let entity_id = uuid::Uuid::new_v4();
|
||||
|
||||
// 70% updates to Transform, 20% to Material, 10% to unique components
|
||||
for i in 0..num_ops {
|
||||
let (component_type, data_size) = match i % 10 {
|
||||
0..=6 => ("Transform".to_string(), 64), // 70%
|
||||
7..=8 => ("Material".to_string(), 128), // 20%
|
||||
_ => (format!("Component{}", i), 32), // 10%
|
||||
};
|
||||
|
||||
let op = PersistenceOp::UpsertComponent {
|
||||
entity_id,
|
||||
component_type,
|
||||
data: vec![i as u8; data_size],
|
||||
};
|
||||
buffer.add(op).ok();
|
||||
}
|
||||
|
||||
black_box(buffer);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: Entity updates (different code path)
|
||||
///
|
||||
/// Tests the performance of entity-level deduplication.
|
||||
fn bench_entity_updates(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("WriteBuffer::add (entity updates)");
|
||||
|
||||
for num_updates in [10, 100, 500] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(num_updates),
|
||||
&num_updates,
|
||||
|b, &num_updates| {
|
||||
b.iter(|| {
|
||||
let mut buffer = WriteBuffer::new(2000);
|
||||
let entity_id = uuid::Uuid::new_v4();
|
||||
|
||||
// Repeatedly update the same entity
|
||||
for i in 0..num_updates {
|
||||
let op = PersistenceOp::UpsertEntity {
|
||||
id: entity_id,
|
||||
data: lib::persistence::EntityData {
|
||||
id: entity_id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
entity_type: format!("Type{}", i),
|
||||
},
|
||||
};
|
||||
buffer.add(op).ok();
|
||||
}
|
||||
|
||||
black_box(buffer);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: take_operations() performance
|
||||
///
|
||||
/// Measures the cost of clearing the buffer and returning operations.
|
||||
fn bench_take_operations(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("WriteBuffer::take_operations");
|
||||
|
||||
for buffer_size in [10, 100, 500, 1000] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(buffer_size),
|
||||
&buffer_size,
|
||||
|b, &buffer_size| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
// Setup: Create a buffer with many operations
|
||||
let mut buffer = WriteBuffer::new(2000);
|
||||
let entity_id = uuid::Uuid::new_v4();
|
||||
|
||||
for i in 0..buffer_size {
|
||||
let op = PersistenceOp::UpsertComponent {
|
||||
entity_id,
|
||||
component_type: format!("Component{}", i),
|
||||
data: vec![i as u8; 64],
|
||||
};
|
||||
buffer.add(op).ok();
|
||||
}
|
||||
|
||||
buffer
|
||||
},
|
||||
|mut buffer| {
|
||||
// Benchmark: Take all operations
|
||||
black_box(buffer.take_operations())
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_repeated_updates_same_component,
|
||||
bench_different_components,
|
||||
bench_mixed_workload,
|
||||
bench_entity_updates,
|
||||
bench_take_operations,
|
||||
);
|
||||
criterion_main!(benches);
|
||||
Reference in New Issue
Block a user