Files
wifi-densepose/crates/ruvector-graph/tests/performance_tests.rs
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

435 lines
11 KiB
Rust

//! Performance and regression tests
//!
//! Benchmark tests to ensure performance doesn't degrade over time.
use ruvector_graph::{Edge, GraphDB, Label, Node, Properties, PropertyValue};
use std::time::Instant;
// ============================================================================
// Baseline Performance Tests
// ============================================================================
#[test]
fn test_node_creation_performance() {
let db = GraphDB::new();
let num_nodes = 10_000;
let start = Instant::now();
for i in 0..num_nodes {
let mut props = Properties::new();
props.insert("id".to_string(), PropertyValue::Integer(i));
let node = Node::new(
format!("node_{}", i),
vec![Label {
name: "Benchmark".to_string(),
}],
props,
);
db.create_node(node).unwrap();
}
let duration = start.elapsed();
println!("Created {} nodes in {:?}", num_nodes, duration);
println!(
"Rate: {:.2} nodes/sec",
num_nodes as f64 / duration.as_secs_f64()
);
// Baseline: Should create at least 10k nodes/sec
assert!(
duration.as_secs() < 5,
"Node creation too slow: {:?}",
duration
);
}
#[test]
fn test_node_retrieval_performance() {
let db = GraphDB::new();
let num_nodes = 10_000;
// Setup
for i in 0..num_nodes {
db.create_node(Node::new(format!("node_{}", i), vec![], Properties::new()))
.unwrap();
}
// Measure retrieval
let start = Instant::now();
for i in 0..num_nodes {
let node = db.get_node(&format!("node_{}", i));
assert!(node.is_some());
}
let duration = start.elapsed();
println!("Retrieved {} nodes in {:?}", num_nodes, duration);
println!(
"Rate: {:.2} reads/sec",
num_nodes as f64 / duration.as_secs_f64()
);
// Should be very fast for in-memory lookups
assert!(
duration.as_secs() < 1,
"Node retrieval too slow: {:?}",
duration
);
}
#[test]
fn test_edge_creation_performance() {
let db = GraphDB::new();
let num_nodes = 1000;
let edges_per_node = 10;
// Create nodes
for i in 0..num_nodes {
db.create_node(Node::new(format!("n{}", i), vec![], Properties::new()))
.unwrap();
}
// Create edges
let start = Instant::now();
for i in 0..num_nodes {
for j in 0..edges_per_node {
let to = (i + j + 1) % num_nodes;
let edge = Edge::new(
format!("e_{}_{}", i, j),
format!("n{}", i),
format!("n{}", to),
"CONNECTS".to_string(),
Properties::new(),
);
db.create_edge(edge).unwrap();
}
}
let duration = start.elapsed();
let total_edges = num_nodes * edges_per_node;
println!("Created {} edges in {:?}", total_edges, duration);
println!(
"Rate: {:.2} edges/sec",
total_edges as f64 / duration.as_secs_f64()
);
}
// TODO: Implement graph traversal methods
// #[test]
// fn test_traversal_performance() {
// let db = GraphDB::new();
// let num_nodes = 1000;
//
// // Create chain
// for i in 0..num_nodes {
// db.create_node(Node::new(format!("n{}", i), vec![], Properties::new())).unwrap();
// }
//
// for i in 0..num_nodes - 1 {
// db.create_edge(Edge::new(
// format!("e{}", i),
// format!("n{}", i),
// format!("n{}", i + 1),
// RelationType { name: "NEXT".to_string() },
// Properties::new(),
// )).unwrap();
// }
//
// // Measure traversal
// let start = Instant::now();
// let path = db.traverse("n0", "NEXT", 100).unwrap();
// let duration = start.elapsed();
//
// assert_eq!(path.len(), 100);
// println!("Traversed 100 hops in {:?}", duration);
// }
// ============================================================================
// Scalability Tests
// ============================================================================
#[test]
fn test_large_graph_creation() {
let db = GraphDB::new();
let num_nodes = 100_000;
let start = Instant::now();
for i in 0..num_nodes {
if i % 10_000 == 0 {
println!("Created {} nodes...", i);
}
let node = Node::new(format!("large_{}", i), vec![], Properties::new());
db.create_node(node).unwrap();
}
let duration = start.elapsed();
println!("Created {} nodes in {:?}", num_nodes, duration);
println!(
"Rate: {:.2} nodes/sec",
num_nodes as f64 / duration.as_secs_f64()
);
}
#[test]
#[ignore] // Long-running test
fn test_million_node_graph() {
let db = GraphDB::new();
let num_nodes = 1_000_000;
let start = Instant::now();
for i in 0..num_nodes {
if i % 100_000 == 0 {
println!("Created {} nodes...", i);
}
let node = Node::new(format!("mega_{}", i), vec![], Properties::new());
db.create_node(node).unwrap();
}
let duration = start.elapsed();
println!("Created {} nodes in {:?}", num_nodes, duration);
println!(
"Rate: {:.2} nodes/sec",
num_nodes as f64 / duration.as_secs_f64()
);
}
// ============================================================================
// Memory Usage Tests
// ============================================================================
#[test]
fn test_memory_efficiency() {
let db = GraphDB::new();
let num_nodes = 10_000;
for i in 0..num_nodes {
let mut props = Properties::new();
props.insert("data".to_string(), PropertyValue::String("x".repeat(100)));
let node = Node::new(format!("mem_{}", i), vec![], props);
db.create_node(node).unwrap();
}
// TODO: Measure actual memory usage
// This would require platform-specific APIs
}
// ============================================================================
// Property-based Performance Tests
// ============================================================================
#[test]
fn test_property_heavy_nodes() {
let db = GraphDB::new();
let num_nodes = 1_000;
let props_per_node = 50;
let start = Instant::now();
for i in 0..num_nodes {
let mut props = Properties::new();
for j in 0..props_per_node {
props.insert(format!("prop_{}", j), PropertyValue::Integer(j as i64));
}
let node = Node::new(format!("heavy_{}", i), vec![], props);
db.create_node(node).unwrap();
}
let duration = start.elapsed();
println!(
"Created {} property-heavy nodes in {:?}",
num_nodes, duration
);
}
// ============================================================================
// Query Performance Tests (TODO)
// ============================================================================
// #[test]
// fn test_simple_query_performance() {
// let db = setup_benchmark_graph(10_000);
//
// let start = Instant::now();
// let results = db.execute("MATCH (n:Person) RETURN n LIMIT 100").unwrap();
// let duration = start.elapsed();
//
// assert_eq!(results.len(), 100);
// println!("Simple query took: {:?}", duration);
// }
// #[test]
// fn test_aggregation_performance() {
// let db = setup_benchmark_graph(100_000);
//
// let start = Instant::now();
// let results = db.execute("MATCH (n:Person) RETURN COUNT(n)").unwrap();
// let duration = start.elapsed();
//
// println!("Aggregation over 100k nodes took: {:?}", duration);
// }
// #[test]
// fn test_join_performance() {
// let db = setup_benchmark_graph(10_000);
//
// let start = Instant::now();
// let results = db.execute("
// MATCH (a:Person)-[:KNOWS]->(b:Person)
// WHERE a.age > 30
// RETURN a, b
// ").unwrap();
// let duration = start.elapsed();
//
// println!("Join query took: {:?}", duration);
// }
// ============================================================================
// Index Performance Tests (TODO)
// ============================================================================
// #[test]
// fn test_indexed_lookup_performance() {
// let db = GraphDB::new();
//
// // Create index
// db.create_index("Person", "email").unwrap();
//
// // Insert data
// for i in 0..100_000 {
// db.execute(&format!(
// "CREATE (:Person {{email: 'user{}@example.com'}})",
// i
// )).unwrap();
// }
//
// // Measure lookup
// let start = Instant::now();
// let results = db.execute("MATCH (n:Person {email: 'user50000@example.com'}) RETURN n").unwrap();
// let duration = start.elapsed();
//
// assert_eq!(results.len(), 1);
// println!("Indexed lookup took: {:?}", duration);
// assert!(duration.as_millis() < 10); // Should be very fast
// }
// ============================================================================
// Regression Tests
// ============================================================================
#[test]
fn test_regression_node_creation() {
let db = GraphDB::new();
let start = Instant::now();
for i in 0..1000 {
db.create_node(Node::new(format!("regr_{}", i), vec![], Properties::new()))
.unwrap();
}
let duration = start.elapsed();
// Baseline threshold - should not regress beyond this
// Adjust based on baseline measurements
assert!(
duration.as_millis() < 500,
"Regression detected: {:?}",
duration
);
}
#[test]
fn test_regression_node_retrieval() {
let db = GraphDB::new();
// Setup
for i in 0..1000 {
db.create_node(Node::new(format!("regr_{}", i), vec![], Properties::new()))
.unwrap();
}
let start = Instant::now();
for i in 0..1000 {
let _ = db.get_node(&format!("regr_{}", i));
}
let duration = start.elapsed();
// Should be very fast
assert!(
duration.as_millis() < 100,
"Regression detected: {:?}",
duration
);
}
// ============================================================================
// Helper Functions
// ============================================================================
#[allow(dead_code)]
fn setup_benchmark_graph(num_nodes: usize) -> GraphDB {
let db = GraphDB::new();
for i in 0..num_nodes {
let mut props = Properties::new();
props.insert(
"name".to_string(),
PropertyValue::String(format!("Person{}", i)),
);
props.insert(
"age".to_string(),
PropertyValue::Integer((20 + (i % 60)) as i64),
);
db.create_node(Node::new(
format!("person_{}", i),
vec![Label {
name: "Person".to_string(),
}],
props,
))
.unwrap();
}
// Create some edges
for i in 0..num_nodes / 10 {
let from = i;
let to = (i + 1) % num_nodes;
db.create_edge(Edge::new(
format!("knows_{}", i),
format!("person_{}", from),
format!("person_{}", to),
"KNOWS".to_string(),
Properties::new(),
))
.unwrap();
}
db
}