Files
herodb/benches/concurrent_ops.rs
Maxime Van Hees 9136e5f3c0 benchmarking
2025-10-30 11:17:26 +01:00

317 lines
13 KiB
Rust

// benches/concurrent_ops.rs
use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId};
use tokio::runtime::Runtime;
use std::sync::Arc;
mod common;
use common::*;
/// Benchmark concurrent write operations
fn bench_concurrent_writes(c: &mut Criterion) {
let mut group = c.benchmark_group("concurrent_ops/writes");
for num_clients in [10, 50] {
for backend_type in BackendType::all() {
let backend = BenchmarkBackend::new(backend_type).expect("Failed to create backend");
let storage = backend.storage.clone();
group.bench_with_input(
BenchmarkId::new(format!("{}/clients", backend.name()), num_clients),
&(storage, num_clients),
|b, (storage, num_clients)| {
let rt = Runtime::new().unwrap();
b.to_async(&rt).iter(|| {
let storage = storage.clone();
let num_clients = *num_clients;
async move {
let mut tasks = Vec::new();
for client_id in 0..num_clients {
let storage = storage.clone();
let task = tokio::spawn(async move {
let mut generator = DataGenerator::new(42 + client_id as u64);
for i in 0..100 {
let key = format!("client:{}:key:{}", client_id, i);
let value = generator.generate_value(100);
storage.set(key, value).unwrap();
}
});
tasks.push(task);
}
for task in tasks {
task.await.unwrap();
}
}
});
}
);
}
}
group.finish();
}
/// Benchmark concurrent read operations
fn bench_concurrent_reads(c: &mut Criterion) {
let mut group = c.benchmark_group("concurrent_ops/reads");
for num_clients in [10, 50] {
for backend_type in BackendType::all() {
// Pre-populate with data
let backend = setup_populated_backend(backend_type, 10_000, 100)
.expect("Failed to setup backend");
let storage = backend.storage.clone();
group.bench_with_input(
BenchmarkId::new(format!("{}/clients", backend.name()), num_clients),
&(storage, num_clients),
|b, (storage, num_clients)| {
let rt = Runtime::new().unwrap();
b.to_async(&rt).iter(|| {
let storage = storage.clone();
let num_clients = *num_clients;
async move {
let mut tasks = Vec::new();
for client_id in 0..num_clients {
let storage = storage.clone();
let task = tokio::spawn(async move {
let generator = DataGenerator::new(42);
for i in 0..100 {
let key_id = (client_id * 100 + i) % 10_000;
let key = generator.generate_key("bench:key", key_id);
storage.get(&key).unwrap();
}
});
tasks.push(task);
}
for task in tasks {
task.await.unwrap();
}
}
});
}
);
}
}
group.finish();
}
/// Benchmark mixed concurrent workload (70% reads, 30% writes)
fn bench_concurrent_mixed(c: &mut Criterion) {
let mut group = c.benchmark_group("concurrent_ops/mixed");
for num_clients in [10, 50] {
for backend_type in BackendType::all() {
// Pre-populate with data
let backend = setup_populated_backend(backend_type, 10_000, 100)
.expect("Failed to setup backend");
let storage = backend.storage.clone();
group.bench_with_input(
BenchmarkId::new(format!("{}/clients", backend.name()), num_clients),
&(storage, num_clients),
|b, (storage, num_clients)| {
let rt = Runtime::new().unwrap();
b.to_async(&rt).iter(|| {
let storage = storage.clone();
let num_clients = *num_clients;
async move {
let mut tasks = Vec::new();
for client_id in 0..num_clients {
let storage = storage.clone();
let task = tokio::spawn(async move {
let mut generator = DataGenerator::new(42 + client_id as u64);
for i in 0..100 {
if i % 10 < 7 {
// 70% reads
let key_id = (client_id * 100 + i) % 10_000;
let key = generator.generate_key("bench:key", key_id);
storage.get(&key).unwrap();
} else {
// 30% writes
let key = format!("client:{}:key:{}", client_id, i);
let value = generator.generate_value(100);
storage.set(key, value).unwrap();
}
}
});
tasks.push(task);
}
for task in tasks {
task.await.unwrap();
}
}
});
}
);
}
}
group.finish();
}
/// Benchmark concurrent hash operations
fn bench_concurrent_hash_ops(c: &mut Criterion) {
let mut group = c.benchmark_group("concurrent_ops/hash_ops");
for num_clients in [10, 50] {
for backend_type in BackendType::all() {
let backend = BenchmarkBackend::new(backend_type).expect("Failed to create backend");
let storage = backend.storage.clone();
group.bench_with_input(
BenchmarkId::new(format!("{}/clients", backend.name()), num_clients),
&(storage, num_clients),
|b, (storage, num_clients)| {
let rt = Runtime::new().unwrap();
b.to_async(&rt).iter(|| {
let storage = storage.clone();
let num_clients = *num_clients;
async move {
let mut tasks = Vec::new();
for client_id in 0..num_clients {
let storage = storage.clone();
let task = tokio::spawn(async move {
let mut generator = DataGenerator::new(42 + client_id as u64);
for i in 0..50 {
let key = format!("client:{}:hash:{}", client_id, i);
let field = format!("field{}", i % 10);
let value = generator.generate_value(100);
storage.hset(&key, vec![(field, value)]).unwrap();
}
});
tasks.push(task);
}
for task in tasks {
task.await.unwrap();
}
}
});
}
);
}
}
group.finish();
}
/// Benchmark concurrent list operations
fn bench_concurrent_list_ops(c: &mut Criterion) {
let mut group = c.benchmark_group("concurrent_ops/list_ops");
for num_clients in [10, 50] {
for backend_type in BackendType::all() {
let backend = BenchmarkBackend::new(backend_type).expect("Failed to create backend");
let storage = backend.storage.clone();
group.bench_with_input(
BenchmarkId::new(format!("{}/clients", backend.name()), num_clients),
&(storage, num_clients),
|b, (storage, num_clients)| {
let rt = Runtime::new().unwrap();
b.to_async(&rt).iter(|| {
let storage = storage.clone();
let num_clients = *num_clients;
async move {
let mut tasks = Vec::new();
for client_id in 0..num_clients {
let storage = storage.clone();
let task = tokio::spawn(async move {
let mut generator = DataGenerator::new(42 + client_id as u64);
for i in 0..50 {
let key = format!("client:{}:list:{}", client_id, i);
let element = generator.generate_value(100);
storage.rpush(&key, vec![element]).unwrap();
}
});
tasks.push(task);
}
for task in tasks {
task.await.unwrap();
}
}
});
}
);
}
}
group.finish();
}
/// Benchmark concurrent scan operations
fn bench_concurrent_scans(c: &mut Criterion) {
let mut group = c.benchmark_group("concurrent_ops/scans");
for num_clients in [10, 50] {
for backend_type in BackendType::all() {
// Pre-populate with data
let backend = setup_populated_backend(backend_type, 10_000, 100)
.expect("Failed to setup backend");
let storage = backend.storage.clone();
group.bench_with_input(
BenchmarkId::new(format!("{}/clients", backend.name()), num_clients),
&(storage, num_clients),
|b, (storage, num_clients)| {
let rt = Runtime::new().unwrap();
b.to_async(&rt).iter(|| {
let storage = storage.clone();
let num_clients = *num_clients;
async move {
let mut tasks = Vec::new();
for _client_id in 0..num_clients {
let storage = storage.clone();
let task = tokio::spawn(async move {
let mut cursor = 0u64;
let mut total = 0;
loop {
let (next_cursor, items) = storage
.scan(cursor, None, Some(100))
.unwrap();
total += items.len();
if next_cursor == 0 {
break;
}
cursor = next_cursor;
}
total
});
tasks.push(task);
}
for task in tasks {
task.await.unwrap();
}
}
});
}
);
}
}
group.finish();
}
criterion_group!(
benches,
bench_concurrent_writes,
bench_concurrent_reads,
bench_concurrent_mixed,
bench_concurrent_hash_ops,
bench_concurrent_list_ops,
bench_concurrent_scans,
);
criterion_main!(benches);