511 lines
15 KiB
Rust
511 lines
15 KiB
Rust
//! Comprehensive benchmarks for nanosecond-scheduler crate
|
|
//!
|
|
//! Benchmarks cover:
|
|
//! - Schedule overhead (target: <100ns)
|
|
//! - Task execution latency
|
|
//! - Priority queue operations
|
|
//! - Statistics calculation overhead
|
|
//! - Multi-threaded scheduling
|
|
//! - Batch operations
|
|
//!
|
|
//! Performance targets:
|
|
//! - Schedule overhead: <100ns
|
|
//! - Task execution: <1μs
|
|
//! - Stats calculation: <10μs
|
|
|
|
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId, Throughput};
|
|
use midstreamer_scheduler::{
|
|
NanoScheduler, Task, TaskPriority, ScheduleResult,
|
|
stats::SchedulerStats,
|
|
};
|
|
use std::time::{Duration, Instant};
|
|
use std::sync::{Arc, Mutex};
|
|
use std::thread;
|
|
|
|
// ============================================================================
|
|
// Test Task Generators
|
|
// ============================================================================
|
|
|
|
fn create_simple_task(id: u64) -> Task {
|
|
Task::new(
|
|
format!("task_{}", id),
|
|
Box::new(move || {
|
|
// Minimal work
|
|
black_box(id * 2);
|
|
}),
|
|
TaskPriority::Normal,
|
|
)
|
|
}
|
|
|
|
fn create_compute_task(id: u64, iterations: u64) -> Task {
|
|
Task::new(
|
|
format!("compute_{}", id),
|
|
Box::new(move || {
|
|
let mut sum = 0u64;
|
|
for i in 0..iterations {
|
|
sum = sum.wrapping_add(i);
|
|
}
|
|
black_box(sum);
|
|
}),
|
|
TaskPriority::Normal,
|
|
)
|
|
}
|
|
|
|
fn create_io_task(id: u64, sleep_micros: u64) -> Task {
|
|
Task::new(
|
|
format!("io_{}", id),
|
|
Box::new(move || {
|
|
thread::sleep(Duration::from_micros(sleep_micros));
|
|
black_box(id);
|
|
}),
|
|
TaskPriority::Normal,
|
|
)
|
|
}
|
|
|
|
fn create_priority_task(id: u64, priority: TaskPriority) -> Task {
|
|
Task::new(
|
|
format!("priority_{}", id),
|
|
Box::new(move || {
|
|
black_box(id);
|
|
}),
|
|
priority,
|
|
)
|
|
}
|
|
|
|
// ============================================================================
|
|
// Schedule Overhead Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_schedule_overhead(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("schedule_overhead");
|
|
group.throughput(Throughput::Elements(1));
|
|
|
|
// Single task scheduling
|
|
group.bench_function("single_task", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
let mut task_id = 0u64;
|
|
|
|
b.iter(|| {
|
|
task_id += 1;
|
|
let task = create_simple_task(task_id);
|
|
black_box(scheduler.schedule(black_box(task)))
|
|
});
|
|
});
|
|
|
|
// Batch scheduling
|
|
for batch_size in [10, 50, 100, 500].iter() {
|
|
group.throughput(Throughput::Elements(*batch_size as u64));
|
|
group.bench_with_input(
|
|
BenchmarkId::new("batch", batch_size),
|
|
batch_size,
|
|
|b, &size| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
let mut task_id = 0u64;
|
|
|
|
b.iter(|| {
|
|
let tasks: Vec<_> = (0..size)
|
|
.map(|_| {
|
|
task_id += 1;
|
|
create_simple_task(task_id)
|
|
})
|
|
.collect();
|
|
|
|
for task in tasks {
|
|
black_box(scheduler.schedule(black_box(task)));
|
|
}
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_schedule_with_priorities(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("schedule_priorities");
|
|
|
|
let priorities = [
|
|
("critical", TaskPriority::Critical),
|
|
("high", TaskPriority::High),
|
|
("normal", TaskPriority::Normal),
|
|
("low", TaskPriority::Low),
|
|
];
|
|
|
|
for (name, priority) in priorities.iter() {
|
|
group.bench_function(*name, |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
let mut task_id = 0u64;
|
|
|
|
b.iter(|| {
|
|
task_id += 1;
|
|
let task = create_priority_task(task_id, *priority);
|
|
black_box(scheduler.schedule(black_box(task)))
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Task Execution Latency Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_execution_latency(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("execution_latency");
|
|
|
|
// Minimal work
|
|
group.bench_function("minimal_work", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
b.iter(|| {
|
|
let task = create_simple_task(1);
|
|
scheduler.schedule(task);
|
|
scheduler.run_once();
|
|
});
|
|
});
|
|
|
|
// Light compute
|
|
group.bench_function("light_compute", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
b.iter(|| {
|
|
let task = create_compute_task(1, 100);
|
|
scheduler.schedule(task);
|
|
scheduler.run_once();
|
|
});
|
|
});
|
|
|
|
// Medium compute
|
|
group.bench_function("medium_compute", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
b.iter(|| {
|
|
let task = create_compute_task(1, 1000);
|
|
scheduler.schedule(task);
|
|
scheduler.run_once();
|
|
});
|
|
});
|
|
|
|
// Heavy compute
|
|
group.bench_function("heavy_compute", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
b.iter(|| {
|
|
let task = create_compute_task(1, 10000);
|
|
scheduler.schedule(task);
|
|
scheduler.run_once();
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_execution_throughput(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("execution_throughput");
|
|
|
|
for num_tasks in [10, 50, 100, 500, 1000].iter() {
|
|
group.throughput(Throughput::Elements(*num_tasks as u64));
|
|
group.bench_with_input(
|
|
BenchmarkId::new("tasks", num_tasks),
|
|
num_tasks,
|
|
|b, &size| {
|
|
b.iter(|| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
for i in 0..size {
|
|
let task = create_simple_task(i as u64);
|
|
scheduler.schedule(task);
|
|
}
|
|
|
|
while scheduler.has_pending_tasks() {
|
|
scheduler.run_once();
|
|
}
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Priority Queue Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_priority_queue_operations(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("priority_queue");
|
|
|
|
// Push operations
|
|
group.bench_function("push_operations", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
let mut task_id = 0u64;
|
|
|
|
b.iter(|| {
|
|
task_id += 1;
|
|
let priority = match task_id % 4 {
|
|
0 => TaskPriority::Critical,
|
|
1 => TaskPriority::High,
|
|
2 => TaskPriority::Normal,
|
|
_ => TaskPriority::Low,
|
|
};
|
|
let task = create_priority_task(task_id, priority);
|
|
black_box(scheduler.schedule(black_box(task)))
|
|
});
|
|
});
|
|
|
|
// Pop operations
|
|
group.bench_function("pop_operations", |b| {
|
|
b.iter(|| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
// Fill queue
|
|
for i in 0..100 {
|
|
let priority = match i % 4 {
|
|
0 => TaskPriority::Critical,
|
|
1 => TaskPriority::High,
|
|
2 => TaskPriority::Normal,
|
|
_ => TaskPriority::Low,
|
|
};
|
|
let task = create_priority_task(i, priority);
|
|
scheduler.schedule(task);
|
|
}
|
|
|
|
// Pop all
|
|
while scheduler.has_pending_tasks() {
|
|
black_box(scheduler.run_once());
|
|
}
|
|
});
|
|
});
|
|
|
|
// Mixed operations
|
|
group.bench_function("mixed_operations", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
let mut task_id = 0u64;
|
|
|
|
b.iter(|| {
|
|
task_id += 1;
|
|
|
|
// Interleave push and pop
|
|
if task_id % 3 == 0 {
|
|
scheduler.run_once();
|
|
} else {
|
|
let priority = match task_id % 4 {
|
|
0 => TaskPriority::Critical,
|
|
1 => TaskPriority::High,
|
|
2 => TaskPriority::Normal,
|
|
_ => TaskPriority::Low,
|
|
};
|
|
let task = create_priority_task(task_id, priority);
|
|
black_box(scheduler.schedule(task));
|
|
}
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Statistics Calculation Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_statistics_overhead(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("statistics");
|
|
|
|
// Stats collection overhead
|
|
group.bench_function("stats_collection", |b| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
// Generate some history
|
|
for i in 0..100 {
|
|
let task = create_compute_task(i, 100);
|
|
scheduler.schedule(task);
|
|
}
|
|
|
|
while scheduler.has_pending_tasks() {
|
|
scheduler.run_once();
|
|
}
|
|
|
|
b.iter(|| {
|
|
black_box(scheduler.get_stats())
|
|
});
|
|
});
|
|
|
|
// Stats calculation with varying history sizes
|
|
for history_size in [10, 50, 100, 500, 1000].iter() {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("history", history_size),
|
|
history_size,
|
|
|b, &size| {
|
|
let mut scheduler = NanoScheduler::new(4);
|
|
|
|
for i in 0..size {
|
|
let task = create_compute_task(i as u64, 100);
|
|
scheduler.schedule(task);
|
|
}
|
|
|
|
while scheduler.has_pending_tasks() {
|
|
scheduler.run_once();
|
|
}
|
|
|
|
b.iter(|| {
|
|
black_box(scheduler.get_stats())
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Multi-threaded Scheduling Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_multithreaded_scheduling(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("multithreaded");
|
|
|
|
for num_threads in [1, 2, 4, 8].iter() {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("threads", num_threads),
|
|
num_threads,
|
|
|b, &threads| {
|
|
b.iter(|| {
|
|
let scheduler = Arc::new(Mutex::new(NanoScheduler::new(threads)));
|
|
let mut handles = vec![];
|
|
|
|
for thread_id in 0..threads {
|
|
let scheduler = Arc::clone(&scheduler);
|
|
let handle = thread::spawn(move || {
|
|
for i in 0..100 {
|
|
let task = create_compute_task(
|
|
thread_id as u64 * 100 + i,
|
|
100
|
|
);
|
|
scheduler.lock().unwrap().schedule(task);
|
|
}
|
|
});
|
|
handles.push(handle);
|
|
}
|
|
|
|
for handle in handles {
|
|
handle.join().unwrap();
|
|
}
|
|
|
|
let mut scheduler = scheduler.lock().unwrap();
|
|
while scheduler.has_pending_tasks() {
|
|
scheduler.run_once();
|
|
}
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_contention_scenarios(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("contention");
|
|
|
|
// High contention
|
|
group.bench_function("high_contention", |b| {
|
|
b.iter(|| {
|
|
let scheduler = Arc::new(Mutex::new(NanoScheduler::new(4)));
|
|
let mut handles = vec![];
|
|
|
|
for _ in 0..8 {
|
|
let scheduler = Arc::clone(&scheduler);
|
|
let handle = thread::spawn(move || {
|
|
for i in 0..50 {
|
|
let task = create_simple_task(i);
|
|
scheduler.lock().unwrap().schedule(task);
|
|
thread::yield_now();
|
|
}
|
|
});
|
|
handles.push(handle);
|
|
}
|
|
|
|
for handle in handles {
|
|
handle.join().unwrap();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Low contention
|
|
group.bench_function("low_contention", |b| {
|
|
b.iter(|| {
|
|
let scheduler = Arc::new(Mutex::new(NanoScheduler::new(4)));
|
|
let mut handles = vec![];
|
|
|
|
for _ in 0..2 {
|
|
let scheduler = Arc::clone(&scheduler);
|
|
let handle = thread::spawn(move || {
|
|
for i in 0..200 {
|
|
let task = create_simple_task(i);
|
|
scheduler.lock().unwrap().schedule(task);
|
|
}
|
|
});
|
|
handles.push(handle);
|
|
}
|
|
|
|
for handle in handles {
|
|
handle.join().unwrap();
|
|
}
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Criterion Configuration
|
|
// ============================================================================
|
|
|
|
criterion_group! {
|
|
name = overhead_benches;
|
|
config = Criterion::default()
|
|
.sample_size(1000)
|
|
.measurement_time(Duration::from_secs(10))
|
|
.warm_up_time(Duration::from_secs(3));
|
|
targets = bench_schedule_overhead, bench_schedule_with_priorities
|
|
}
|
|
|
|
criterion_group! {
|
|
name = latency_benches;
|
|
config = Criterion::default()
|
|
.sample_size(200)
|
|
.measurement_time(Duration::from_secs(10));
|
|
targets = bench_execution_latency, bench_execution_throughput
|
|
}
|
|
|
|
criterion_group! {
|
|
name = queue_benches;
|
|
config = Criterion::default()
|
|
.sample_size(500)
|
|
.measurement_time(Duration::from_secs(8));
|
|
targets = bench_priority_queue_operations
|
|
}
|
|
|
|
criterion_group! {
|
|
name = stats_benches;
|
|
config = Criterion::default()
|
|
.sample_size(500);
|
|
targets = bench_statistics_overhead
|
|
}
|
|
|
|
criterion_group! {
|
|
name = threading_benches;
|
|
config = Criterion::default()
|
|
.sample_size(50)
|
|
.measurement_time(Duration::from_secs(15));
|
|
targets = bench_multithreaded_scheduling, bench_contention_scenarios
|
|
}
|
|
|
|
criterion_main!(
|
|
overhead_benches,
|
|
latency_benches,
|
|
queue_benches,
|
|
stats_benches,
|
|
threading_benches
|
|
);
|