use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::fs;
use std::time::Duration;
use wasm4pm::advanced_algorithms::{
analyze_infrequent_paths, compute_model_metrics, detect_bottlenecks, detect_rework,
};
use wasm4pm::analysis::analyze_dotted_chart;
use wasm4pm::fast_discovery::{analyze_activity_cooccurrence, analyze_start_end_activities};
use wasm4pm::final_analytics::{
analyze_process_speedup, analyze_temporal_bottlenecks, analyze_variant_complexity,
compute_activity_transition_matrix, compute_trace_similarity_matrix, extract_activity_ordering,
};
use wasm4pm::more_discovery::analyze_activity_dependencies;
use wasm4pm::xes_format::validate_and_parse_xes;
#[path = "helpers.rs"]
mod helpers;
use helpers::{store_log, ACTIVITY_KEY, TIMESTAMP_KEY};
struct RealDataset {
label: &'static str,
handle: String,
events: usize,
}
fn load_real_dataset(label: &'static str, candidates: &[&str]) -> Option<RealDataset> {
for path in candidates {
let content = match fs::read_to_string(path) {
Ok(c) if c.len() > 200 => c,
_ => continue,
};
let log = match validate_and_parse_xes(&content) {
Ok(l) if !l.traces.is_empty() => l,
_ => continue,
};
let events = log.event_count();
let handle = store_log(log);
return Some(RealDataset {
label,
handle,
events,
});
}
None
}
fn real_datasets() -> Vec<RealDataset> {
let mut sets = Vec::new();
if let Some(ds) = load_real_dataset(
"sepsis",
&["bench_data/sepsis.xes", "../bench_data/sepsis.xes"],
) {
sets.push(ds);
}
if let Some(ds) = load_real_dataset(
"bpi2020",
&[
"bench_data/bpi2020_travel.xes",
"../bench_data/bpi2020_travel.xes",
],
) {
sets.push(ds);
}
if let Some(ds) = load_real_dataset(
"roadtraffic",
&[
"bench_data/roadtraffic100traces.xes",
"../bench_data/roadtraffic100traces.xes",
],
) {
sets.push(ds);
}
assert!(
!sets.is_empty(),
"analytics bench requires at least bench_data/sepsis.xes ā no real dataset found"
);
sets
}
fn configure(group: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>) {
if helpers::is_fast_mode() {
helpers::fast_group(group);
} else {
group.measurement_time(Duration::from_secs(5));
group.warm_up_time(Duration::from_millis(500));
group.sample_size(20);
}
}
fn configure_slow(group: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>) {
if helpers::is_fast_mode() {
helpers::fast_group(group);
} else {
group.measurement_time(Duration::from_secs(20));
group.warm_up_time(Duration::from_secs(3));
group.sample_size(10);
}
}
fn bench_detect_rework(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/detect_rework");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| black_box(detect_rework(black_box(h), black_box(ACTIVITY_KEY)).unwrap()))
});
}
group.finish();
}
fn bench_detect_bottlenecks(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/detect_bottlenecks");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
detect_bottlenecks(
black_box(h),
black_box(ACTIVITY_KEY),
black_box(TIMESTAMP_KEY),
black_box(60),
)
.unwrap(),
)
})
});
}
group.finish();
}
fn bench_model_metrics(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/model_metrics");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(compute_model_metrics(black_box(h), black_box(ACTIVITY_KEY)).unwrap())
})
});
}
group.finish();
}
fn bench_infrequent_paths(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/infrequent_paths");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
analyze_infrequent_paths(black_box(h), black_box(ACTIVITY_KEY), black_box(0.1))
.unwrap(),
)
})
});
}
group.finish();
}
fn bench_variant_complexity(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/variant_complexity");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
analyze_variant_complexity(black_box(h), black_box(ACTIVITY_KEY)).unwrap(),
)
})
});
}
group.finish();
}
fn bench_transition_matrix(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/transition_matrix");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
compute_activity_transition_matrix(black_box(h), black_box(ACTIVITY_KEY))
.unwrap(),
)
})
});
}
group.finish();
}
fn bench_process_speedup(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/process_speedup");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
analyze_process_speedup(black_box(h), black_box(TIMESTAMP_KEY), black_box(50))
.unwrap(),
)
})
});
}
group.finish();
}
fn bench_temporal_bottlenecks(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/temporal_bottlenecks");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
analyze_temporal_bottlenecks(
black_box(h),
black_box(ACTIVITY_KEY),
black_box(TIMESTAMP_KEY),
)
.unwrap(),
)
})
});
}
group.finish();
}
fn bench_activity_ordering(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/activity_ordering");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(extract_activity_ordering(black_box(h), black_box(ACTIVITY_KEY)).unwrap())
})
});
}
group.finish();
}
fn bench_trace_similarity(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/trace_similarity");
configure_slow(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
compute_trace_similarity_matrix(black_box(h), black_box(ACTIVITY_KEY)).unwrap(),
)
})
});
}
group.finish();
}
fn bench_activity_cooccurrence(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/activity_cooccurrence");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
analyze_activity_cooccurrence(black_box(h), black_box(ACTIVITY_KEY)).unwrap(),
)
})
});
}
group.finish();
}
fn bench_start_end_activities(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/start_end_activities");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
analyze_start_end_activities(black_box(h), black_box(ACTIVITY_KEY)).unwrap(),
)
})
});
}
group.finish();
}
fn bench_activity_dependencies(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/activity_dependencies");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| {
black_box(
analyze_activity_dependencies(black_box(h), black_box(ACTIVITY_KEY)).unwrap(),
)
})
});
}
group.finish();
}
fn bench_dotted_chart(c: &mut Criterion) {
let datasets = real_datasets();
let mut group = c.benchmark_group("analytics/dotted_chart");
configure(&mut group);
for ds in &datasets {
group.throughput(Throughput::Elements(ds.events as u64));
group.bench_with_input(BenchmarkId::new("log", ds.label), &ds.handle, |b, h| {
b.iter(|| black_box(analyze_dotted_chart(black_box(h)).unwrap()))
});
}
group.finish();
}
criterion_group!(
analytics_benches,
bench_detect_rework,
bench_detect_bottlenecks,
bench_model_metrics,
bench_infrequent_paths,
bench_variant_complexity,
bench_transition_matrix,
bench_process_speedup,
bench_temporal_bottlenecks,
bench_activity_ordering,
bench_trace_similarity,
bench_activity_cooccurrence,
bench_start_end_activities,
bench_activity_dependencies,
bench_dotted_chart,
);
criterion_main!(analytics_benches);