use serde_json::json;
use std::time::Instant;
use uni_db::{Uni, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("๐ฌ Query Parameter Benchmark: String Interpolation vs Parameterized\n");
println!("Testing: 50 CREATE queries with 128-dim embeddings\n");
const ITERATIONS: usize = 50;
let embedding: Vec<f32> = (0..128).map(|x| x as f32).collect();
let db1 = Uni::open("/tmp/uni_bench1").build().await?;
let db2 = Uni::open("/tmp/uni_bench2").build().await?;
println!("๐ Approach 1: String Interpolation (current)\n");
println!(" Query example:");
let embedding_str = json!(embedding).to_string();
let example = format!(
"CREATE (n:Person {{name: 'Bench_0', age: 30, embedding: {}}})",
&embedding_str[..60] );
println!(" {}", example);
println!(
" Query length: ~{} bytes\n",
format!(
"CREATE (n:Person {{name: 'Bench_0', age: 30, embedding: {}}})",
embedding_str
)
.len()
);
let start = Instant::now();
for i in 0..ITERATIONS {
let embedding_str = json!(embedding).to_string();
let cypher = format!(
"CREATE (n:Person {{name: 'Bench_{}', age: 30, embedding: {}}})",
i, embedding_str
);
let s = db1.session();
let tx = s.tx().await?;
tx.execute(&cypher).await?;
tx.commit().await?;
}
let duration1 = start.elapsed();
let per_query1 = duration1.as_micros() as f64 / ITERATIONS as f64;
println!("โ
Completed: {} queries in {:?}", ITERATIONS, duration1);
println!(" Average: {:.1}ยตs per query\n", per_query1);
println!("---\n");
println!("๐ Approach 2: Parameterized Queries\n");
println!(" Query example:");
println!(" CREATE (n:Person {{name: $name, age: $age, embedding: $embedding}})");
println!(" Query length: ~70 bytes");
println!(" + Parameters passed as structured data\n");
let embedding_value = Value::List(embedding.iter().map(|&f| Value::Float(f as f64)).collect());
let start = Instant::now();
let cypher = "CREATE (n:Person {name: $name, age: $age, embedding: $embedding})";
for i in 0..ITERATIONS {
db2.session()
.query_with(cypher)
.param("name", Value::String(format!("Bench_{}", i)))
.param("age", Value::Int(30))
.param("embedding", embedding_value.clone())
.fetch_all()
.await?;
}
let duration2 = start.elapsed();
let per_query2 = duration2.as_micros() as f64 / ITERATIONS as f64;
println!("โ
Completed: {} queries in {:?}", ITERATIONS, duration2);
println!(" Average: {:.1}ยตs per query\n", per_query2);
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
println!("\n๐ RESULTS\n");
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ");
println!("โ Approach โ Total Time โ Per Query โ");
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโค");
println!(
"โ String Interpolation โ {:>9.2}ms โ {:>9.1}ยตs โ",
duration1.as_micros() as f64 / 1000.0,
per_query1
);
println!(
"โ Parameterized Queries โ {:>9.2}ms โ {:>9.1}ยตs โ",
duration2.as_micros() as f64 / 1000.0,
per_query2
);
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ");
let speedup = per_query1 / per_query2;
let savings_us = per_query1 - per_query2;
let savings_pct = (savings_us / per_query1) * 100.0;
println!("\n๐ฏ Performance Improvement:");
println!(" Speedup: {:.2}x faster", speedup);
println!(
" Savings: {:.1}ยตs per query ({:.1}% reduction)",
savings_us, savings_pct
);
if savings_pct > 5.0 {
println!("\nโ
Parameterized queries are significantly faster!");
} else {
println!("\nโ ๏ธ Difference is small - other factors may dominate.");
}
println!("\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
println!("\n๐ Overhead Breakdown (String Interpolation):\n");
let start = Instant::now();
for _ in 0..ITERATIONS {
let _embedding_str = json!(embedding).to_string();
}
let json_overhead = start.elapsed().as_micros() as f64 / ITERATIONS as f64;
println!(" JSON serialization: {:.1}ยตs", json_overhead);
let embedding_str = json!(embedding).to_string();
let start = Instant::now();
for i in 0..ITERATIONS {
let _cypher = format!(
"CREATE (n:Person {{name: 'Bench_{}', age: 30, embedding: {}}})",
i, embedding_str
);
}
let format_overhead = start.elapsed().as_micros() as f64 / ITERATIONS as f64;
println!(" String concatenation: {:.1}ยตs", format_overhead);
let parse_execute_overhead = per_query1 - json_overhead - format_overhead;
println!(" Parse + Execute: {:.1}ยตs", parse_execute_overhead);
println!(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
println!(" Total: {:.1}ยตs\n", per_query1);
println!("๐ Overhead Breakdown (Parameterized):\n");
let start = Instant::now();
for i in 0..ITERATIONS {
let _name = Value::String(format!("Bench_{}", i));
let _age = Value::Int(30);
let _embedding = embedding_value.clone();
}
let param_overhead = start.elapsed().as_micros() as f64 / ITERATIONS as f64;
println!(" Parameter preparation: {:.1}ยตs", param_overhead);
let parse_execute_overhead2 = per_query2 - param_overhead;
println!(
" Parse + Execute: {:.1}ยตs",
parse_execute_overhead2
);
println!(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
println!(" Total: {:.1}ยตs\n", per_query2);
println!("๐ก Key Insight:");
let parsing_savings = parse_execute_overhead - parse_execute_overhead2;
if parsing_savings > 0.0 {
println!(
" Parsing is {:.1}ยตs faster with parameters ({:.1}% reduction)",
parsing_savings,
(parsing_savings / parse_execute_overhead) * 100.0
);
println!(" This confirms that parsing large array literals is expensive!");
}
Ok(())
}