ruvector-graph 2.2.0

Distributed Neo4j-compatible hypergraph database with SIMD optimization
Documentation
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use ruvector_graph::types::{EdgeId, NodeId, Properties, PropertyValue};
use ruvector_graph::{Edge, GraphDB, Node};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;

/// Helper to create test graph
fn create_test_graph() -> GraphDB {
    GraphDB::new()
}

/// Benchmark: Single node insertion
fn bench_node_insertion_single(c: &mut Criterion) {
    let mut group = c.benchmark_group("node_insertion_single");

    for size in [1, 10, 100, 1000].iter() {
        group.throughput(Throughput::Elements(*size as u64));
        group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
            b.iter(|| {
                let graph = create_test_graph();
                for i in 0..size {
                    let mut props = Properties::new();
                    props.insert(
                        "name".to_string(),
                        PropertyValue::String(format!("node_{}", i)),
                    );
                    props.insert("value".to_string(), PropertyValue::Integer(i as i64));

                    let node_id = NodeId(format!("node_{}", i));
                    let node = Node::new(node_id, vec!["Person".to_string()], props);
                    black_box(graph.create_node(node).unwrap());
                }
            });
        });
    }

    group.finish();
}

/// Benchmark: Batch node insertion
fn bench_node_insertion_batch(c: &mut Criterion) {
    let mut group = c.benchmark_group("node_insertion_batch");

    for batch_size in [100, 1000, 10000].iter() {
        group.throughput(Throughput::Elements(*batch_size as u64));
        group.bench_with_input(
            BenchmarkId::from_parameter(batch_size),
            batch_size,
            |b, &batch_size| {
                b.iter(|| {
                    let graph = create_test_graph();
                    for i in 0..batch_size {
                        let mut props = Properties::new();
                        props.insert(
                            "name".to_string(),
                            PropertyValue::String(format!("node_{}", i)),
                        );
                        props.insert("value".to_string(), PropertyValue::Integer(i as i64));

                        let node_id = NodeId(format!("batch_node_{}", i));
                        let node = Node::new(node_id, vec!["Person".to_string()], props);
                        black_box(graph.create_node(node).unwrap());
                    }
                });
            },
        );
    }

    group.finish();
}

/// Benchmark: Bulk node insertion (optimized path)
fn bench_node_insertion_bulk(c: &mut Criterion) {
    let mut group = c.benchmark_group("node_insertion_bulk");
    group.sample_size(10); // Reduce samples for large operations

    for bulk_size in [10000, 100000].iter() {
        group.throughput(Throughput::Elements(*bulk_size as u64));
        group.bench_with_input(
            BenchmarkId::from_parameter(bulk_size),
            bulk_size,
            |b, &bulk_size| {
                b.iter(|| {
                    let graph = create_test_graph();
                    for i in 0..bulk_size {
                        let mut props = Properties::new();
                        props.insert("id".to_string(), PropertyValue::Integer(i as i64));
                        props.insert(
                            "name".to_string(),
                            PropertyValue::String(format!("user_{}", i)),
                        );

                        let node_id = NodeId(format!("bulk_user_{}", i));
                        let node = Node::new(node_id, vec!["User".to_string()], props);
                        black_box(graph.create_node(node).unwrap());
                    }
                });
            },
        );
    }

    group.finish();
}

/// Benchmark: Edge creation
fn bench_edge_creation(c: &mut Criterion) {
    let mut group = c.benchmark_group("edge_creation");

    // Setup: Create nodes once
    let graph = Arc::new(create_test_graph());
    let mut node_ids = Vec::new();
    for i in 0..1000 {
        let mut props = Properties::new();
        props.insert("id".to_string(), PropertyValue::Integer(i as i64));
        let node_id = NodeId(format!("edge_test_node_{}", i));
        let node = Node::new(node_id.clone(), vec!["Person".to_string()], props);
        graph.create_node(node).unwrap();
        node_ids.push(node_id);
    }

    for num_edges in [100, 1000].iter() {
        group.throughput(Throughput::Elements(*num_edges as u64));
        group.bench_with_input(
            BenchmarkId::from_parameter(num_edges),
            num_edges,
            |b, &num_edges| {
                let graph = graph.clone();
                let node_ids = node_ids.clone();
                b.iter(|| {
                    for i in 0..num_edges {
                        let from = &node_ids[i % node_ids.len()];
                        let to = &node_ids[(i + 1) % node_ids.len()];

                        let mut props = Properties::new();
                        props.insert("weight".to_string(), PropertyValue::Float(i as f64));

                        let edge_id = EdgeId(format!("edge_{}", i));
                        let edge = Edge::new(
                            edge_id,
                            from.clone(),
                            to.clone(),
                            "KNOWS".to_string(),
                            props,
                        );
                        black_box(graph.create_edge(edge).unwrap());
                    }
                });
            },
        );
    }

    group.finish();
}

/// Benchmark: Simple node lookup by ID
fn bench_query_node_lookup(c: &mut Criterion) {
    let mut group = c.benchmark_group("query_node_lookup");

    // Setup: Create 10k nodes (reduced for faster benchmark)
    let graph = Arc::new(create_test_graph());
    let mut node_ids = Vec::new();
    for i in 0..10000 {
        let mut props = Properties::new();
        props.insert("id".to_string(), PropertyValue::Integer(i as i64));
        let node_id = NodeId(format!("lookup_node_{}", i));
        let node = Node::new(node_id.clone(), vec!["Person".to_string()], props);
        graph.create_node(node).unwrap();
        node_ids.push(node_id);
    }

    group.bench_function("lookup_by_id", |b| {
        let graph = graph.clone();
        let node_ids = node_ids.clone();
        b.iter(|| {
            let id = &node_ids[black_box(1234 % node_ids.len())];
            black_box(graph.get_node(id).unwrap());
        });
    });

    group.finish();
}

/// Benchmark: Edge lookup
fn bench_query_edge_lookup(c: &mut Criterion) {
    let mut group = c.benchmark_group("query_edge_lookup");

    // Setup: Create nodes and edges
    let graph = Arc::new(create_test_graph());
    let mut node_ids = Vec::new();
    let mut edge_ids = Vec::new();

    // Create 100 nodes
    for i in 0..100 {
        let mut props = Properties::new();
        props.insert("id".to_string(), PropertyValue::Integer(i as i64));
        let node_id = NodeId(format!("trav_node_{}", i));
        let node = Node::new(node_id.clone(), vec!["Person".to_string()], props);
        graph.create_node(node).unwrap();
        node_ids.push(node_id);
    }

    // Create edges (each node has ~5 outgoing edges)
    for i in 0..node_ids.len() {
        for j in 0..5 {
            let to_idx = (i + j + 1) % node_ids.len();
            let edge_id = EdgeId(format!("trav_edge_{}_{}", i, j));
            let edge = Edge::new(
                edge_id.clone(),
                node_ids[i].clone(),
                node_ids[to_idx].clone(),
                "KNOWS".to_string(),
                Properties::new(),
            );
            graph.create_edge(edge).unwrap();
            edge_ids.push(edge_id);
        }
    }

    group.bench_function("edge_by_id", |b| {
        let graph = graph.clone();
        let edge_ids = edge_ids.clone();
        b.iter(|| {
            let id = &edge_ids[black_box(10 % edge_ids.len())];
            black_box(graph.get_edge(id).unwrap());
        });
    });

    group.finish();
}

/// Benchmark: Get nodes by label
fn bench_query_get_by_label(c: &mut Criterion) {
    let mut group = c.benchmark_group("query_get_by_label");

    let graph = Arc::new(create_test_graph());

    // Create diverse nodes with different labels
    for i in 0..1000 {
        let mut props = Properties::new();
        props.insert("id".to_string(), PropertyValue::Integer(i as i64));
        let node_id = NodeId(format!("label_node_{}", i));

        let label = if i % 3 == 0 {
            "Person"
        } else if i % 3 == 1 {
            "Organization"
        } else {
            "Location"
        };

        let node = Node::new(node_id, vec![label.to_string()], props);
        graph.create_node(node).unwrap();
    }

    group.bench_function("get_persons", |b| {
        let graph = graph.clone();
        b.iter(|| {
            let nodes = graph.get_nodes_by_label("Person");
            black_box(nodes.len());
        });
    });

    group.finish();
}

/// Benchmark: Memory usage tracking
fn bench_memory_usage(c: &mut Criterion) {
    let mut group = c.benchmark_group("memory_usage");
    group.sample_size(10);

    for num_nodes in [1000, 10000].iter() {
        group.throughput(Throughput::Elements(*num_nodes as u64));
        group.bench_with_input(
            BenchmarkId::from_parameter(num_nodes),
            num_nodes,
            |b, &num_nodes| {
                b.iter_custom(|iters| {
                    let mut total_duration = Duration::ZERO;

                    for _ in 0..iters {
                        let graph = create_test_graph();

                        let start = std::time::Instant::now();
                        for i in 0..*num_nodes {
                            let mut props = Properties::new();
                            props.insert("id".to_string(), PropertyValue::Integer(i as i64));
                            props.insert(
                                "name".to_string(),
                                PropertyValue::String(format!("node_{}", i)),
                            );

                            let node_id = NodeId(format!("mem_node_{}", i));
                            let node = Node::new(node_id, vec!["TestNode".to_string()], props);
                            graph.create_node(node).unwrap();
                        }
                        total_duration += start.elapsed();

                        // Force drop to measure cleanup
                        drop(graph);
                    }

                    total_duration
                });
            },
        );
    }

    group.finish();
}

criterion_group!(
    benches,
    bench_node_insertion_single,
    bench_node_insertion_batch,
    bench_node_insertion_bulk,
    bench_edge_creation,
    bench_query_node_lookup,
    bench_query_edge_lookup,
    bench_query_get_by_label,
    bench_memory_usage
);

criterion_main!(benches);