graph_d 1.3.2

A native graph database implementation in Rust with built-in JSON support and SQLite-like simplicity
Documentation
//! Performance test for large graph operations.

use graph_d::{Graph, Result};
use serde_json::json;
use std::time::Instant;

fn main() -> Result<()> {
    println!("=== Graph Database Performance Test ===\n");

    // Test different graph sizes
    let test_sizes = vec![1_000, 10_000, 100_000];

    for &size in &test_sizes {
        println!("Testing with {size} nodes...");
        test_graph_performance(size)?;
        println!();
    }

    println!("=== Memory Efficiency Test ===");
    test_memory_efficiency()?;

    Ok(())
}

fn test_graph_performance(node_count: usize) -> Result<()> {
    let start = Instant::now();

    // Create graph
    let mut graph = Graph::new()?;
    println!("  Graph initialization: {:?}", start.elapsed());

    // Node creation benchmark
    let node_start = Instant::now();
    let mut node_ids = Vec::with_capacity(node_count);

    for i in 0..node_count {
        let properties = [
            ("id".to_string(), json!(i)),
            ("name".to_string(), json!(format!("Node_{}", i))),
            (
                "type".to_string(),
                json!(if i % 2 == 0 { "even" } else { "odd" }),
            ),
            ("category".to_string(), json!(i % 100)),
            ("score".to_string(), json!(i as f64 * 0.123)),
            (
                "metadata".to_string(),
                json!({
                    "created_at": "2024-01-01",
                    "version": 1,
                    "tags": [format!("tag_{}", i % 10)]
                }),
            ),
        ]
        .into();

        let node_id = graph.create_node(properties)?;
        node_ids.push(node_id);
    }

    let node_duration = node_start.elapsed();
    println!("  Node creation ({node_count} nodes): {node_duration:?}");
    println!(
        "  Nodes per second: {:.0}",
        node_count as f64 / node_duration.as_secs_f64()
    );

    // Relationship creation benchmark
    let rel_start = Instant::now();
    let rel_count = std::cmp::min(node_count * 2, 50_000); // Limit relationships for performance

    for i in 0..rel_count {
        let from_idx = i % node_count;
        let to_idx = (i + 1) % node_count;

        let properties = [
            ("weight".to_string(), json!(i as f64 / 1000.0)),
            ("created".to_string(), json!("2024-01-01")),
        ]
        .into();

        graph.create_relationship(
            node_ids[from_idx],
            node_ids[to_idx],
            "CONNECTS".to_string(),
            properties,
        )?;
    }

    let rel_duration = rel_start.elapsed();
    println!("  Relationship creation ({rel_count} rels): {rel_duration:?}");
    println!(
        "  Relationships per second: {:.0}",
        rel_count as f64 / rel_duration.as_secs_f64()
    );

    // Read performance benchmark
    let read_start = Instant::now();
    let read_operations = std::cmp::min(1000, node_count);

    for i in 0..read_operations {
        let node_id = node_ids[i % node_count];
        let _node = graph.get_node(node_id)?;
        let _rels = graph.get_relationships_for_node(node_id)?;
    }

    let read_duration = read_start.elapsed();
    println!("  Read operations ({read_operations}): {read_duration:?}");
    println!(
        "  Reads per second: {:.0}",
        read_operations as f64 / read_duration.as_secs_f64()
    );

    // Overall statistics
    let total_duration = start.elapsed();
    println!("  Total time: {total_duration:?}");
    println!(
        "  Final graph size: {} nodes, {} relationships",
        graph.storage.node_count(),
        graph.storage.relationship_count()
    );

    Ok(())
}

fn test_memory_efficiency() -> Result<()> {
    println!("Testing memory efficiency with realistic data sizes...");

    let start = Instant::now();
    let mut graph = Graph::new()?;

    // Create a smaller but more realistic dataset
    const NODES: usize = 50_000;
    const RELS_PER_NODE: usize = 4;

    println!("  Creating {NODES} nodes with rich properties...");
    let mut node_ids = Vec::with_capacity(NODES);

    for i in 0..NODES {
        // Create nodes with realistic JSON properties
        let properties = [
            ("id".to_string(), json!(i)),
            ("name".to_string(), json!(format!("User_{}", i))),
            ("email".to_string(), json!(format!("user{}@example.com", i))),
            ("age".to_string(), json!(20 + (i % 60))),
            ("location".to_string(), json!(format!("City_{}", i % 1000))),
            ("interests".to_string(), json!(vec![
                format!("interest_{}", i % 20),
                format!("hobby_{}", (i * 3) % 15)
            ])),
            ("profile".to_string(), json!({
                "bio": format!("This is user {} with some biographical information that takes up space", i),
                "settings": {
                    "notifications": true,
                    "privacy": "public",
                    "theme": if i % 2 == 0 { "dark" } else { "light" }
                },
                "stats": {
                    "posts": i % 100,
                    "followers": i % 1000,
                    "following": (i * 2) % 500
                }
            })),
        ].into();

        let node_id = graph.create_node(properties)?;
        node_ids.push(node_id);
    }

    println!("  Node creation completed in {:?}", start.elapsed());

    // Create relationships
    let rel_start = Instant::now();

    for i in 0..NODES {
        for j in 1..=RELS_PER_NODE {
            if i + j < NODES {
                let properties = [
                    (
                        "type".to_string(),
                        json!(match j {
                            1 => "FOLLOWS",
                            2 => "FRIENDS_WITH",
                            3 => "WORKS_WITH",
                            _ => "KNOWS",
                        }),
                    ),
                    (
                        "strength".to_string(),
                        json!(j as f64 / RELS_PER_NODE as f64),
                    ),
                    (
                        "since".to_string(),
                        json!(format!("2024-{:02}-01", (i % 12) + 1)),
                    ),
                ]
                .into();

                graph.create_relationship(
                    node_ids[i],
                    node_ids[i + j],
                    "SOCIAL".to_string(),
                    properties,
                )?;
            }
        }
    }

    println!(
        "  Relationship creation completed in {:?}",
        rel_start.elapsed()
    );

    // Performance tests
    let query_start = Instant::now();
    let mut total_results = 0;

    for i in (0..NODES).step_by(1000) {
        let node_id = node_ids[i];
        let _node = graph.get_node(node_id)?;
        let rels = graph.get_relationships_for_node(node_id)?;
        total_results += rels.len();
    }

    println!(
        "  Query performance test completed in {:?}",
        query_start.elapsed()
    );
    println!(
        "  Average relationships per sampled node: {:.1}",
        total_results as f64 / (NODES / 1000) as f64
    );

    let total_time = start.elapsed();
    println!("  Total test time: {total_time:?}");
    println!(
        "  Final size: {} nodes, {} relationships",
        graph.storage.node_count(),
        graph.storage.relationship_count()
    );

    // Calculate estimated memory usage
    let estimated_memory_mb = estimate_memory_usage(&graph);
    println!("  Estimated memory usage: {estimated_memory_mb:.1} MB");
    println!(
        "  Memory per node: {:.1} KB",
        (estimated_memory_mb * 1024.0) / NODES as f64
    );

    Ok(())
}

fn estimate_memory_usage(graph: &Graph) -> f64 {
    // Rough estimation based on data structures
    let node_count = graph.storage.node_count();
    let rel_count = graph.storage.relationship_count();

    // Estimated sizes (very rough):
    // - Node with properties: ~500 bytes average (JSON + overhead)
    // - Relationship with properties: ~200 bytes average
    // - Index overhead: ~50% of data size

    let node_memory = node_count as f64 * 500.0;
    let rel_memory = rel_count as f64 * 200.0;
    let index_overhead = (node_memory + rel_memory) * 0.5;

    (node_memory + rel_memory + index_overhead) / (1024.0 * 1024.0)
}