reputation-core 0.1.0

Core calculation engine for the KnowThat Reputation System with advanced scoring algorithms
Documentation
//! Performance optimization guide for the reputation engine
//! 
//! This example demonstrates best practices for maximizing performance
//! when using the reputation calculation engine.

use reputation_core::{Calculator, BatchOptions};
use reputation_types::AgentDataBuilder;
use std::time::Instant;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Reputation Engine Performance Guide ===\n");
    
    // Generate test data
    let agents = generate_test_agents(10_000);
    
    // 1. Single vs Batch Processing
    println!("1. SINGLE vs BATCH PROCESSING");
    println!("-----------------------------");
    compare_single_vs_batch(&agents)?;
    
    // 2. Optimal Chunk Sizes
    println!("\n2. OPTIMAL CHUNK SIZES");
    println!("----------------------");
    test_chunk_sizes(&agents)?;
    
    // 3. Calculator Reuse
    println!("\n3. CALCULATOR REUSE");
    println!("-------------------");
    demonstrate_calculator_reuse(&agents)?;
    
    // 4. Memory Efficiency
    println!("\n4. MEMORY EFFICIENCY");
    println!("--------------------");
    demonstrate_memory_efficiency()?;
    
    // 5. Performance Tips
    println!("\n5. PERFORMANCE TIPS");
    println!("-------------------");
    print_performance_tips();
    
    Ok(())
}

fn generate_test_agents(count: usize) -> Vec<reputation_types::AgentData> {
    (0..count)
        .map(|i| {
            let reviews = (i % 100) as u32;
            let mut builder = AgentDataBuilder::new(&format!("did:perf:agent{}", i))
                .total_interactions(reviews + (i % 50) as u32);
            
            if reviews > 0 {
                builder = builder.with_reviews(reviews, 3.0 + (i % 20) as f64 * 0.1);
            }
            
            if i % 10 == 0 {
                builder = builder.mcp_level((i % 4) as u8);
            }
            
            builder.build().unwrap()
        })
        .collect()
}

fn compare_single_vs_batch(agents: &[reputation_types::AgentData]) -> Result<(), Box<dyn std::error::Error>> {
    let calc = Calculator::default();
    let test_size = 1000;
    let test_agents = &agents[..test_size.min(agents.len())];
    
    // Single processing
    let start = Instant::now();
    for agent in test_agents {
        let _ = calc.calculate(agent)?;
    }
    let single_duration = start.elapsed();
    
    // Batch processing
    let start = Instant::now();
    let _ = calc.calculate_batch(test_agents);
    let batch_duration = start.elapsed();
    
    println!("Processing {} agents:", test_size);
    println!("  Single (sequential): {:?}", single_duration);
    println!("  Batch (parallel):    {:?}", batch_duration);
    println!("  Speedup:             {:.2}x", 
        single_duration.as_secs_f64() / batch_duration.as_secs_f64());
    
    // Per-agent timing
    println!("\nPer-agent timing:");
    println!("  Single: {:?}/agent", single_duration / test_size as u32);
    println!("  Batch:  {:?}/agent", batch_duration / test_size as u32);
    
    Ok(())
}

fn test_chunk_sizes(agents: &[reputation_types::AgentData]) -> Result<(), Box<dyn std::error::Error>> {
    let calc = Calculator::default();
    let test_size = 5000;
    let test_agents = &agents[..test_size.min(agents.len())];
    
    let chunk_sizes = vec![10, 50, 100, 200, 500, 1000];
    let cpu_count = 8; // Typical CPU count
    
    println!("Testing chunk sizes (CPUs: {}):", cpu_count);
    println!("Chunk Size | Duration  | Per Agent | Efficiency");
    println!("-----------|-----------|-----------|------------");
    
    for chunk_size in chunk_sizes {
        let options = BatchOptions {
            chunk_size: Some(chunk_size),
            fail_fast: false,
            progress_callback: None,
        };
        
        let start = Instant::now();
        let result = calc.calculate_batch_with_options(test_agents, options);
        let duration = start.elapsed();
        
        let per_agent = duration.as_micros() as f64 / test_size as f64;
        let efficiency = result.successful_count as f64 / test_size as f64 * 100.0;
        
        println!("{:10} | {:9.2?} | {:7.1}μs | {:6.1}%",
            chunk_size, duration, per_agent, efficiency);
    }
    
    println!("\nRecommendation: Use chunk_size = {} (CPUs * 10-20)",
        cpu_count * 10);
    
    Ok(())
}

fn demonstrate_calculator_reuse(agents: &[reputation_types::AgentData]) -> Result<(), Box<dyn std::error::Error>> {
    let test_size = 1000;
    let test_agents = &agents[..test_size.min(agents.len())];
    
    // Creating new calculator each time (inefficient)
    let start = Instant::now();
    for agent in test_agents {
        let calc = Calculator::default(); // Don't do this!
        let _ = calc.calculate(agent)?;
    }
    let new_calc_duration = start.elapsed();
    
    // Reusing calculator (efficient)
    let calc = Calculator::default();
    let start = Instant::now();
    for agent in test_agents {
        let _ = calc.calculate(agent)?;
    }
    let reuse_duration = start.elapsed();
    
    println!("Calculator instantiation overhead:");
    println!("  New calculator each time: {:?}", new_calc_duration);
    println!("  Reused calculator:        {:?}", reuse_duration);
    println!("  Overhead:                 {:?}", new_calc_duration - reuse_duration);
    println!("  Overhead per agent:       {:.1}μs",
        (new_calc_duration - reuse_duration).as_micros() as f64 / test_size as f64);
    
    Ok(())
}

fn demonstrate_memory_efficiency() -> Result<(), Box<dyn std::error::Error>> {
    println!("Memory characteristics:");
    
    // Size of key structures
    println!("  Calculator size:        {} bytes", std::mem::size_of::<Calculator>());
    println!("  AgentData size:         {} bytes", std::mem::size_of::<reputation_types::AgentData>());
    println!("  ReputationScore size:   {} bytes", std::mem::size_of::<reputation_types::ReputationScore>());
    
    // Demonstrate zero-allocation calculation
    let calc = Calculator::default();
    let agent = AgentDataBuilder::new("did:test:memory")
        .with_reviews(100, 4.0)
        .total_interactions(150)
        .build()?;
    
    // The calculate method doesn't allocate on the heap
    let _score = calc.calculate(&agent)?;
    
    println!("\nMemory efficiency features:");
    println!("  ✓ Zero heap allocations during calculation");
    println!("  ✓ Stack-based computation");
    println!("  ✓ Efficient for embedded systems");
    println!("  ✓ Cache-friendly data layout");
    
    Ok(())
}

fn print_performance_tips() {
    println!("Key Performance Tips:");
    println!();
    println!("1. **Use Batch Processing**");
    println!("   - Up to 10x faster for multiple agents");
    println!("   - Automatically uses all CPU cores");
    println!();
    println!("2. **Optimal Chunk Size**");
    println!("   - Set chunk_size to CPU_COUNT * 10-20");
    println!("   - Balances parallelism and overhead");
    println!();
    println!("3. **Reuse Calculator Instances**");
    println!("   - Create once, use many times");
    println!("   - Thread-safe with Arc for sharing");
    println!();
    println!("4. **Progress Callbacks**");
    println!("   - Use sparingly - adds overhead");
    println!("   - Update UI every N agents, not every agent");
    println!();
    println!("5. **Data Preparation**");
    println!("   - Validate data before batch processing");
    println!("   - Use fail_fast: true if data quality is uncertain");
    println!();
    
    println!("Example optimal configuration:");
    println!("```rust");
    println!("let options = BatchOptions {{");
    println!("    chunk_size: Some(num_cpus::get() * 10),");
    println!("    fail_fast: false,");
    println!("    progress_callback: Some(Box::new(|completed, total| {{");
    println!("        if completed % 100 == 0 {{  // Update every 100");
    println!("            update_progress_bar(completed, total);");
    println!("        }}");
    println!("    }})),");
    println!("}};");
    println!("```");
}