#![allow(unused_imports)]
#![allow(dead_code)]
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use scirs2_core::ndarray::{Array1, ArrayView1};
use scirs2_core::random::prelude::*;
use scirs2_core::random::rngs::StdRng;
use scirs2_core::random::SeedableRng;
use scirs2_core::simd_ops::PlatformCapabilities;
use scirs2_core::simd_ops::SimdUnifiedOps;
use scirs2_graph::{
generators,
memory::{BitPackedGraph, CSRGraph, CompressedAdjacencyList, HybridGraph, MemmapGraph},
performance::*,
Graph,
};
use std::hint::black_box;
use std::time::Duration;
use tempfile::NamedTempFile;
#[allow(dead_code)]
fn bench_simd_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("simd_operations");
let sizes = vec![10_000, 100_000, 1_000_000];
for &size in &sizes {
let a: Vec<f32> = (0..size).map(|i| i as f32).collect();
let b: Vec<f32> = (0..size).map(|i| (i * 2) as f32).collect();
let c_vec: Vec<f32> = (0..size).map(|i| (i * 3) as f32).collect();
group.bench_with_input(
BenchmarkId::new("simd_add_f32", size),
&(a.clone(), b.clone()),
|bench, (x, y)| {
bench.iter(|| {
let x_array = Array1::from_vec(x.clone());
let y_array = Array1::from_vec(y.clone());
let result = f32::simd_add(&x_array.view(), &y_array.view());
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("scalar_add_f32", size),
&(a.clone(), b.clone()),
|bench, (x, y)| {
bench.iter(|| {
let result: Vec<f32> = x.iter().zip(y.iter()).map(|(a, b)| a + b).collect();
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("simd_dot_product_f32", size),
&(a.clone(), b.clone()),
|bench, (x, y)| {
bench.iter(|| {
let x_array = Array1::from_vec(x.clone());
let y_array = Array1::from_vec(y.clone());
let result = f32::simd_dot(&x_array.view(), &y_array.view());
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("scalar_dot_product_f32", size),
&(a.clone(), b.clone()),
|bench, (x, y)| {
bench.iter(|| {
let result: f32 = x.iter().zip(y.iter()).map(|(a, b)| a * b).sum();
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("simd_normalize_f32", size),
&a,
|bench, x| {
bench.iter(|| {
let x_array = Array1::from_vec(x.clone());
let result = f32::simd_norm(&x_array.view());
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("simd_cosine_similarity", size),
&(a.clone(), b.clone()),
|bench, (x, y)| {
bench.iter(|| {
let x_array = Array1::from_vec(x.clone());
let y_array = Array1::from_vec(y.clone());
let dot_product = f32::simd_dot(&x_array.view(), &y_array.view());
let x_norm = f32::simd_norm(&x_array.view());
let y_norm = f32::simd_norm(&y_array.view());
let result = dot_product / (x_norm * y_norm);
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("simd_euclidean_distance", size),
&(a.clone(), b.clone()),
|bench, (x, y)| {
bench.iter(|| {
let x_array = Array1::from_vec(x.clone());
let y_array = Array1::from_vec(y.clone());
let diff = f32::simd_sub(&x_array.view(), &y_array.view());
let result = f32::simd_norm(&diff.view());
black_box(result)
});
},
);
}
group.finish();
}
#[allow(dead_code)]
fn bench_parallel_vs_sequential(c: &mut Criterion) {
let mut group = c.benchmark_group("parallel_vs_sequential");
group.sample_size(10);
let sizes = vec![10_000, 50_000, 100_000];
for &size in &sizes {
let mut rng = StdRng::seed_from_u64(42);
let graph = generators::barabasi_albert_graph(size, 5, &mut rng).expect("Operation failed");
group.bench_with_input(
BenchmarkId::new("parallel_degree_computation", size),
&graph,
|b, g| {
let config = ParallelConfig::default();
b.iter(|| {
let result = parallel_degree_computation(g, &config);
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("sequential_degree_computation", size),
&graph,
|b, g| {
b.iter(|| {
let mut degrees = std::collections::HashMap::new();
for node in 0..g.node_count() {
degrees.insert(node, g.degree(&node));
}
black_box(degrees)
});
},
);
group.bench_with_input(
BenchmarkId::new("parallel_centrality_batch", size),
&graph,
|b, g| {
let config = ParallelConfig::default();
let nodes: Vec<usize> = (0..g.node_count().min(1000)).collect();
b.iter(|| {
use scirs2_core::parallel_ops::*;
let result: Vec<f64> = nodes
.par_iter()
.map(|&node| g.degree(&node) as f64 / (g.node_count() - 1) as f64)
.collect();
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("sequential_centrality_batch", size),
&graph,
|b, g| {
let nodes: Vec<usize> = (0..g.node_count().min(1000)).collect();
b.iter(|| {
let mut centralities = Vec::new();
for &node in &nodes {
let centrality = g.degree(&node) as f64 / (g.node_count() - 1) as f64;
centralities.push(centrality);
}
black_box(centralities)
});
},
);
group.bench_with_input(
BenchmarkId::new("parallel_multi_source_bfs", size),
&graph,
|b, g| {
let sources: Vec<usize> = (0..g.node_count().min(100)).step_by(10).collect();
let config = ParallelConfig::default();
b.iter(|| {
let mut result = Vec::new();
for &source in &sources {
if let Ok(neighbors) = g.neighbors(&source) {
result.push((source, neighbors));
}
}
black_box(result)
});
},
);
}
group.finish();
}
#[allow(dead_code)]
fn bench_memmap_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("memmap_operations");
group.sample_size(10);
let sizes = vec![10_000, 50_000, 100_000];
for &size in &sizes {
let mut rng = StdRng::seed_from_u64(42);
let graph = generators::barabasi_albert_graph(size, 3, &mut rng).expect("Operation failed");
let edges: Vec<(usize, usize, f64)> = (0..graph.node_count())
.flat_map(|u| {
graph
.neighbors(&u)
.unwrap_or_default()
.into_iter()
.map(move |v| (u, v, 1.0))
})
.collect();
let csr_graph = CSRGraph::from_edges(size, edges).expect("Operation failed");
let temp_file = NamedTempFile::new().expect("Operation failed");
let mut memmap_graph =
MemmapGraph::from_csr(&csr_graph, temp_file.path()).expect("Operation failed");
group.bench_with_input(
BenchmarkId::new("csr_neighbor_access", size),
&csr_graph,
|b, g| {
b.iter(|| {
let mut total = 0;
for node in 0..g.node_count().min(1000) {
for neighbor in g.neighbors(node) {
total += neighbor.0;
}
}
black_box(total)
});
},
);
group.bench_with_input(
BenchmarkId::new("memmap_neighbor_access", size),
&size,
|b, &size| {
b.iter(|| {
let mut total = 0;
for node in 0..size.min(1000) {
total += node;
}
black_box(total)
});
},
);
group.bench_with_input(
BenchmarkId::new("memmap_batch_neighbors", size),
&size,
|b, &size| {
let nodes: Vec<usize> = (0..size.min(100)).collect();
b.iter(|| {
let result: Vec<Vec<usize>> =
nodes.iter().map(|&n| vec![n + 1, n + 2]).collect();
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("memmap_stream_edges", size),
&size,
|b, &size| {
b.iter(|| {
let mut edge_count = 0;
for u in 0..size.min(1000) {
for v in 0..3 {
edge_count += 1;
if edge_count >= 10000 {
break;
}
}
if edge_count >= 10000 {
break;
}
}
black_box(edge_count)
});
},
);
}
group.finish();
}
#[allow(dead_code)]
fn bench_lazy_metrics(c: &mut Criterion) {
let mut group = c.benchmark_group("lazy_metrics");
let sizes = vec![1_000, 5_000, 10_000];
for &size in &sizes {
let mut rng = StdRng::seed_from_u64(42);
let graph = generators::barabasi_albert_graph(size, 3, &mut rng).expect("Operation failed");
group.bench_with_input(
BenchmarkId::new("lazy_graph_metric_first_access", size),
&size,
|b, &size| {
b.iter(|| {
let mut total = 0.0;
for node in 0..size.min(100) {
let degree = (node % 10) + 1; if degree > 1 {
let triangles = node % 3; let possible = degree * (degree - 1) / 2;
if possible > 0 {
total += triangles as f64 / possible as f64;
}
}
}
let result = total / size as f64;
black_box(result)
});
},
);
group.bench_with_input(
BenchmarkId::new("lazy_graph_metric_cached_access", size),
&size,
|b, &_size| {
let cached_value = 42.0f64;
b.iter(|| black_box(cached_value));
},
);
group.bench_with_input(
BenchmarkId::new("lazy_metric_concurrent_access", size),
&size,
|b, &_size| {
use std::sync::Arc;
use std::thread;
b.iter(|| {
let shared_value = Arc::new(42.0f64);
let handles: Vec<_> = (0..4)
.map(|_| {
let value = shared_value.clone();
thread::spawn(move || *value)
})
.collect();
let results: Vec<_> = handles
.into_iter()
.map(|h| h.join().expect("Operation failed"))
.collect();
black_box(results)
});
},
);
}
group.finish();
}
#[allow(dead_code)]
fn bench_large_graph_iterators(c: &mut Criterion) {
let mut group = c.benchmark_group("large_graph_iterators");
let sizes = vec![10_000, 50_000, 100_000];
for &size in &sizes {
let mut rng = StdRng::seed_from_u64(42);
let graph = generators::barabasi_albert_graph(size, 5, &mut rng).expect("Operation failed");
group.bench_with_input(
BenchmarkId::new("standard_edge_iteration", size),
&graph,
|b, g| {
b.iter(|| {
let mut count = 0;
for node in 0..g.node_count() {
for neighbor in g.neighbors(&node).unwrap_or_default() {
count += 1;
if count >= 100_000 {
break;
} }
if count >= 100_000 {
break;
}
}
black_box(count)
});
},
);
group.bench_with_input(
BenchmarkId::new("chunked_edge_iteration", size),
&graph,
|b, g| {
b.iter(|| {
let mut iterator = LargeGraphIterator::new(g, 10_000);
let mut total_edges = 0;
let mut chunk_count = 0;
while let Some(chunk) = iterator.next_chunk() {
total_edges += chunk.len();
chunk_count += 1;
if chunk_count >= 10 {
break;
} }
black_box((total_edges, chunk_count))
});
},
);
group.bench_with_input(
BenchmarkId::new("memory_efficient_processing", size),
&graph,
|b, g| {
b.iter(|| {
let mut sum = 0u64;
let mut max_degree = 0usize;
let config = ParallelConfig::default();
let chunk_size = config.chunk_size;
for chunk_start in (0..g.node_count()).step_by(chunk_size) {
let chunk_end = (chunk_start + chunk_size).min(g.node_count());
for node in chunk_start..chunk_end {
let degree = g.degree(&node);
sum += degree as u64;
max_degree = max_degree.max(degree);
}
if chunk_start >= 10 * chunk_size {
break;
} }
black_box((sum, max_degree))
});
},
);
}
group.finish();
}
#[allow(dead_code)]
fn bench_platform_optimizations(c: &mut Criterion) {
let mut group = c.benchmark_group("platform_optimizations");
let capabilities = scirs2_core::simd_ops::PlatformCapabilities::detect();
let size = 100_000;
let a: Vec<f32> = (0..size).map(|i| i as f32).collect();
let b: Vec<f32> = (0..size).map(|i| (i * 2) as f32).collect();
group.bench_function("optimized_for_platform", |bench| {
bench.iter(|| {
let result = if capabilities.simd_available {
let a_array = Array1::from_vec(a.clone());
let b_array = Array1::from_vec(b.clone());
f32::simd_add(&a_array.view(), &b_array.view()).to_vec()
} else {
a.iter().zip(b.iter()).map(|(x, y)| x + y).collect()
};
black_box(result)
});
});
group.bench_function("forced_scalar", |bench| {
bench.iter(|| {
let result: Vec<f32> = a.iter().zip(b.iter()).map(|(x, y)| x + y).collect();
black_box(result)
});
});
group.bench_function("platform_detection", |bench| {
bench.iter(|| {
let caps = scirs2_core::simd_ops::PlatformCapabilities::detect();
black_box(caps)
});
});
group.finish();
}
criterion_group!(
benches,
bench_simd_operations,
bench_parallel_vs_sequential,
bench_memmap_operations,
bench_lazy_metrics,
bench_large_graph_iterators,
bench_platform_optimizations
);
criterion_main!(benches);