initial arhitectural overhaul
Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
302
crates/libmarathon/benches/vector_clock.rs
Normal file
302
crates/libmarathon/benches/vector_clock.rs
Normal file
@@ -0,0 +1,302 @@
|
||||
//! VectorClock performance benchmarks
|
||||
//!
|
||||
//! These benchmarks measure the performance improvements from the two-pass →
|
||||
//! single-pass optimization in the happened_before() method.
|
||||
|
||||
use criterion::{
|
||||
BenchmarkId,
|
||||
Criterion,
|
||||
black_box,
|
||||
criterion_group,
|
||||
criterion_main,
|
||||
};
|
||||
use libmarathon::networking::VectorClock;
|
||||
|
||||
/// Helper to create a vector clock with N nodes
|
||||
fn create_clock_with_nodes(num_nodes: usize) -> VectorClock {
|
||||
let mut clock = VectorClock::new();
|
||||
|
||||
for i in 0..num_nodes {
|
||||
let node_id = uuid::Uuid::from_u128(i as u128);
|
||||
clock.increment(node_id);
|
||||
}
|
||||
|
||||
clock
|
||||
}
|
||||
|
||||
/// Benchmark: happened_before comparison with small clocks (5 nodes)
|
||||
///
|
||||
/// This is the common case in typical distributed systems with a small number
|
||||
/// of replicas.
|
||||
fn bench_happened_before_small_clocks(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("VectorClock::happened_before (5 nodes)");
|
||||
|
||||
group.bench_function("happened_before (true)", |b| {
|
||||
let node = uuid::Uuid::from_u128(1);
|
||||
|
||||
let mut clock1 = VectorClock::new();
|
||||
clock1.increment(node);
|
||||
|
||||
let mut clock2 = VectorClock::new();
|
||||
clock2.increment(node);
|
||||
clock2.increment(node);
|
||||
|
||||
b.iter(|| black_box(clock1.happened_before(&clock2)));
|
||||
});
|
||||
|
||||
group.bench_function("happened_before (false)", |b| {
|
||||
let node1 = uuid::Uuid::from_u128(1);
|
||||
let node2 = uuid::Uuid::from_u128(2);
|
||||
|
||||
let mut clock1 = VectorClock::new();
|
||||
clock1.increment(node1);
|
||||
|
||||
let mut clock2 = VectorClock::new();
|
||||
clock2.increment(node2);
|
||||
|
||||
b.iter(|| black_box(clock1.happened_before(&clock2)));
|
||||
});
|
||||
|
||||
group.bench_function("happened_before (concurrent)", |b| {
|
||||
let node1 = uuid::Uuid::from_u128(1);
|
||||
let node2 = uuid::Uuid::from_u128(2);
|
||||
let node3 = uuid::Uuid::from_u128(3);
|
||||
|
||||
let mut clock1 = create_clock_with_nodes(5);
|
||||
clock1.increment(node1);
|
||||
|
||||
let mut clock2 = create_clock_with_nodes(5);
|
||||
clock2.increment(node2);
|
||||
clock2.increment(node3);
|
||||
|
||||
b.iter(|| black_box(clock1.happened_before(&clock2)));
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: happened_before comparison with large clocks (100 nodes)
|
||||
///
|
||||
/// This tests the optimization's effectiveness with larger clock sizes.
|
||||
/// The single-pass algorithm should show better improvements here.
|
||||
fn bench_happened_before_large_clocks(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("VectorClock::happened_before (100 nodes)");
|
||||
|
||||
group.bench_function("happened_before (100 nodes, true)", |b| {
|
||||
let clock1 = create_clock_with_nodes(100);
|
||||
|
||||
let mut clock2 = clock1.clone();
|
||||
let node = uuid::Uuid::from_u128(50);
|
||||
clock2.increment(node);
|
||||
|
||||
b.iter(|| black_box(clock1.happened_before(&clock2)));
|
||||
});
|
||||
|
||||
group.bench_function("happened_before (100 nodes, concurrent)", |b| {
|
||||
let mut clock1 = create_clock_with_nodes(100);
|
||||
let node1 = uuid::Uuid::from_u128(200);
|
||||
clock1.increment(node1);
|
||||
|
||||
let mut clock2 = create_clock_with_nodes(100);
|
||||
let node2 = uuid::Uuid::from_u128(201);
|
||||
clock2.increment(node2);
|
||||
|
||||
b.iter(|| black_box(clock1.happened_before(&clock2)));
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: happened_before with disjoint node sets
|
||||
///
|
||||
/// This is the scenario where early exit optimization provides the most
|
||||
/// benefit.
|
||||
fn bench_happened_before_disjoint(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("VectorClock::happened_before (disjoint)");
|
||||
|
||||
for num_nodes in [5, 20, 50, 100] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(num_nodes),
|
||||
&num_nodes,
|
||||
|b, &num_nodes| {
|
||||
// Create two clocks with completely different nodes
|
||||
let mut clock1 = VectorClock::new();
|
||||
for i in 0..num_nodes {
|
||||
clock1.increment(uuid::Uuid::from_u128(i as u128));
|
||||
}
|
||||
|
||||
let mut clock2 = VectorClock::new();
|
||||
for i in 0..num_nodes {
|
||||
clock2.increment(uuid::Uuid::from_u128((i + 1000) as u128));
|
||||
}
|
||||
|
||||
b.iter(|| black_box(clock1.happened_before(&clock2)));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: merge operation
|
||||
fn bench_merge(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("VectorClock::merge");
|
||||
|
||||
for num_nodes in [5, 20, 50, 100] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(num_nodes),
|
||||
&num_nodes,
|
||||
|b, &num_nodes| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
// Setup: Create two clocks with overlapping nodes
|
||||
let clock1 = create_clock_with_nodes(num_nodes);
|
||||
let clock2 = create_clock_with_nodes(num_nodes / 2);
|
||||
(clock1, clock2)
|
||||
},
|
||||
|(mut clock1, clock2)| {
|
||||
clock1.merge(&clock2);
|
||||
black_box(clock1)
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: increment operation
|
||||
fn bench_increment(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("VectorClock::increment");
|
||||
|
||||
group.bench_function("increment (new node)", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let clock = VectorClock::new();
|
||||
let node_id = uuid::Uuid::new_v4();
|
||||
(clock, node_id)
|
||||
},
|
||||
|(mut clock, node_id)| {
|
||||
black_box(clock.increment(node_id));
|
||||
clock
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
|
||||
group.bench_function("increment (existing node)", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let node_id = uuid::Uuid::new_v4();
|
||||
let mut clock = VectorClock::new();
|
||||
clock.increment(node_id);
|
||||
(clock, node_id)
|
||||
},
|
||||
|(mut clock, node_id)| {
|
||||
black_box(clock.increment(node_id));
|
||||
clock
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: is_concurrent_with operation
|
||||
fn bench_is_concurrent(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("VectorClock::is_concurrent_with");
|
||||
|
||||
group.bench_function("concurrent (5 nodes)", |b| {
|
||||
let node1 = uuid::Uuid::from_u128(1);
|
||||
let node2 = uuid::Uuid::from_u128(2);
|
||||
|
||||
let mut clock1 = VectorClock::new();
|
||||
clock1.increment(node1);
|
||||
|
||||
let mut clock2 = VectorClock::new();
|
||||
clock2.increment(node2);
|
||||
|
||||
b.iter(|| black_box(clock1.is_concurrent_with(&clock2)));
|
||||
});
|
||||
|
||||
group.bench_function("not concurrent (ordered)", |b| {
|
||||
let node = uuid::Uuid::from_u128(1);
|
||||
|
||||
let mut clock1 = VectorClock::new();
|
||||
clock1.increment(node);
|
||||
|
||||
let mut clock2 = VectorClock::new();
|
||||
clock2.increment(node);
|
||||
clock2.increment(node);
|
||||
|
||||
b.iter(|| black_box(clock1.is_concurrent_with(&clock2)));
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Benchmark: Realistic workload simulation
|
||||
///
|
||||
/// Simulates a typical distributed system workflow with increments, merges,
|
||||
/// and comparisons.
|
||||
fn bench_realistic_workload(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("VectorClock::realistic_workload");
|
||||
|
||||
group.bench_function("3 nodes, 100 operations", |b| {
|
||||
let node1 = uuid::Uuid::from_u128(1);
|
||||
let node2 = uuid::Uuid::from_u128(2);
|
||||
let node3 = uuid::Uuid::from_u128(3);
|
||||
|
||||
b.iter(|| {
|
||||
let mut clock1 = VectorClock::new();
|
||||
let mut clock2 = VectorClock::new();
|
||||
let mut clock3 = VectorClock::new();
|
||||
|
||||
// Simulate 100 operations across 3 nodes
|
||||
for i in 0..100 {
|
||||
match i % 7 {
|
||||
| 0 => {
|
||||
clock1.increment(node1);
|
||||
},
|
||||
| 1 => {
|
||||
clock2.increment(node2);
|
||||
},
|
||||
| 2 => {
|
||||
clock3.increment(node3);
|
||||
},
|
||||
| 3 => {
|
||||
clock1.merge(&clock2);
|
||||
},
|
||||
| 4 => {
|
||||
clock2.merge(&clock3);
|
||||
},
|
||||
| 5 => {
|
||||
let _ = clock1.happened_before(&clock2);
|
||||
},
|
||||
| _ => {
|
||||
let _ = clock2.is_concurrent_with(&clock3);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
black_box((clock1, clock2, clock3))
|
||||
});
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_happened_before_small_clocks,
|
||||
bench_happened_before_large_clocks,
|
||||
bench_happened_before_disjoint,
|
||||
bench_merge,
|
||||
bench_increment,
|
||||
bench_is_concurrent,
|
||||
bench_realistic_workload,
|
||||
);
|
||||
criterion_main!(benches);
|
||||
Reference in New Issue
Block a user