Files
wifi-densepose/vendor/ruvector/benches/plaid_performance.rs

576 lines
18 KiB
Rust

// Plaid ZK Proof & Learning Performance Benchmarks
//
// Run with: cargo bench --bench plaid_performance
//
// Expected results:
// - Proof generation: ~8μs per proof (32-bit range)
// - Transaction processing: ~1.5μs per transaction
// - Feature extraction: ~0.1μs
// - LSH hashing: ~0.05μs
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId, Throughput};
use ruvector_edge::plaid::*;
use ruvector_edge::plaid::zkproofs::{RangeProof, PedersenCommitment, FinancialProofBuilder};
use std::collections::HashMap;
// ============================================================================
// Proof Generation Benchmarks
// ============================================================================
fn bench_proof_generation(c: &mut Criterion) {
let mut group = c.benchmark_group("proof_generation");
// Test different range sizes (affects bit count and proof complexity)
for range_bits in [8, 16, 32, 64] {
let max = if range_bits == 64 {
u64::MAX / 2 // Avoid overflow
} else {
(1u64 << range_bits) - 1
};
let value = max / 2;
let blinding = PedersenCommitment::random_blinding();
group.throughput(Throughput::Elements(1));
group.bench_with_input(
BenchmarkId::new("range_proof", range_bits),
&(value, max, blinding),
|b, (v, m, bl)| {
b.iter(|| {
RangeProof::prove(
black_box(*v),
0,
black_box(*m),
bl,
)
});
},
);
}
group.finish();
}
fn bench_proof_verification(c: &mut Criterion) {
let mut group = c.benchmark_group("proof_verification");
// Pre-generate proofs of different sizes
let proofs: Vec<_> = [8, 16, 32, 64]
.iter()
.map(|&bits| {
let max = if bits == 64 {
u64::MAX / 2
} else {
(1u64 << bits) - 1
};
let value = max / 2;
let blinding = PedersenCommitment::random_blinding();
(bits, RangeProof::prove(value, 0, max, &blinding).unwrap())
})
.collect();
for (bits, proof) in &proofs {
group.throughput(Throughput::Elements(1));
group.bench_with_input(
BenchmarkId::new("verify", bits),
proof,
|b, p| {
b.iter(|| RangeProof::verify(black_box(p)));
},
);
}
group.finish();
}
fn bench_pedersen_commitment(c: &mut Criterion) {
let mut group = c.benchmark_group("pedersen_commitment");
let value = 50000u64;
let blinding = PedersenCommitment::random_blinding();
group.bench_function("commit", |b| {
b.iter(|| {
PedersenCommitment::commit(black_box(value), black_box(&blinding))
});
});
group.bench_function("verify_opening", |b| {
let commitment = PedersenCommitment::commit(value, &blinding);
b.iter(|| {
PedersenCommitment::verify_opening(
black_box(&commitment),
black_box(value),
black_box(&blinding),
)
});
});
group.finish();
}
fn bench_financial_proofs(c: &mut Criterion) {
let mut group = c.benchmark_group("financial_proofs");
let builder = FinancialProofBuilder::new()
.with_income(vec![6500, 6500, 6800, 6500])
.with_balances(vec![5000, 5200, 4800, 5100, 5300, 5000, 5500]);
group.bench_function("prove_income_above", |b| {
b.iter(|| {
builder.prove_income_above(black_box(5000))
});
});
group.bench_function("prove_affordability", |b| {
b.iter(|| {
builder.prove_affordability(black_box(2000), black_box(3))
});
});
group.bench_function("prove_no_overdrafts", |b| {
b.iter(|| {
builder.prove_no_overdrafts(black_box(30))
});
});
group.bench_function("prove_savings_above", |b| {
b.iter(|| {
builder.prove_savings_above(black_box(4000))
});
});
group.finish();
}
// ============================================================================
// Learning Algorithm Benchmarks
// ============================================================================
fn bench_feature_extraction(c: &mut Criterion) {
let mut group = c.benchmark_group("feature_extraction");
let tx = Transaction {
transaction_id: "tx123".to_string(),
account_id: "acc456".to_string(),
amount: 50.0,
date: "2024-03-15".to_string(),
name: "Starbucks Coffee Shop".to_string(),
merchant_name: Some("Starbucks".to_string()),
category: vec!["Food".to_string(), "Coffee".to_string()],
pending: false,
payment_channel: "in_store".to_string(),
};
group.throughput(Throughput::Elements(1));
group.bench_function("extract_features", |b| {
b.iter(|| extract_features(black_box(&tx)));
});
group.bench_function("to_embedding", |b| {
let features = extract_features(&tx);
b.iter(|| features.to_embedding());
});
group.bench_function("full_pipeline", |b| {
b.iter(|| {
let features = extract_features(black_box(&tx));
features.to_embedding()
});
});
group.finish();
}
fn bench_lsh_hashing(c: &mut Criterion) {
let mut group = c.benchmark_group("lsh_hashing");
let test_cases = vec![
("Short", "Starbucks"),
("Medium", "Amazon.com Services LLC"),
("Long", "Whole Foods Market Store #12345 Manhattan"),
("VeryLong", "Shell Gas Station #12345 - 123 Main Street, City Name, State 12345"),
];
for (name, text) in &test_cases {
group.throughput(Throughput::Bytes(text.len() as u64));
group.bench_with_input(
BenchmarkId::new("simple_lsh", name),
text,
|b, t| {
b.iter(|| {
// LSH is internal, so we extract features which calls it
let tx = Transaction {
transaction_id: "tx".to_string(),
account_id: "acc".to_string(),
amount: 50.0,
date: "2024-01-01".to_string(),
name: t.to_string(),
merchant_name: Some(t.to_string()),
category: vec!["Test".to_string()],
pending: false,
payment_channel: "online".to_string(),
};
extract_features(black_box(&tx))
});
},
);
}
group.finish();
}
fn bench_q_learning(c: &mut Criterion) {
let mut group = c.benchmark_group("q_learning");
let mut state = FinancialLearningState::default();
// Pre-populate with some Q-values
for i in 0..100 {
let key = format!("category_{}|under_budget", i % 10);
state.q_values.insert(key, 0.5 + (i as f64 * 0.01));
}
group.bench_function("update_q_value", |b| {
b.iter(|| {
update_q_value(
black_box(&state),
"Food",
"under_budget",
1.0,
0.1,
)
});
});
group.bench_function("get_recommendation", |b| {
b.iter(|| {
get_recommendation(
black_box(&state),
"Food",
500.0,
600.0,
)
});
});
group.bench_function("q_value_lookup", |b| {
b.iter(|| {
black_box(&state).q_values.get("category_5|under_budget")
});
});
group.finish();
}
// ============================================================================
// End-to-End Transaction Processing
// ============================================================================
fn bench_transaction_processing(c: &mut Criterion) {
let mut group = c.benchmark_group("transaction_processing");
// Test different batch sizes
for batch_size in [1, 10, 100, 1000] {
let transactions: Vec<Transaction> = (0..batch_size)
.map(|i| Transaction {
transaction_id: format!("tx{}", i),
account_id: "acc456".to_string(),
amount: 50.0 + (i as f64 % 100.0),
date: format!("2024-03-{:02}", (i % 28) + 1),
name: format!("Merchant {}", i % 20),
merchant_name: Some(format!("Merchant {}", i % 20)),
category: vec![
format!("Category {}", i % 5),
"Subcategory".to_string()
],
pending: false,
payment_channel: if i % 2 == 0 { "in_store" } else { "online" }.to_string(),
})
.collect();
group.throughput(Throughput::Elements(batch_size as u64));
group.bench_with_input(
BenchmarkId::new("feature_extraction_batch", batch_size),
&transactions,
|b, txs| {
b.iter(|| {
for tx in txs {
let _ = extract_features(black_box(tx));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("full_pipeline_batch", batch_size),
&transactions,
|b, txs| {
b.iter(|| {
for tx in txs {
let features = extract_features(black_box(tx));
let _ = features.to_embedding();
}
});
},
);
}
group.finish();
}
// ============================================================================
// Serialization Benchmarks
// ============================================================================
fn bench_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("serialization");
// Create states with varying sizes
for tx_count in [100, 1000, 10000] {
let mut state = FinancialLearningState::default();
// Populate state to simulate real usage
for i in 0..tx_count {
let category_key = format!("category_{}", i % 10);
let pattern = SpendingPattern {
pattern_id: format!("pat_{}", i),
category: category_key.clone(),
avg_amount: 50.0 + (i as f64 % 100.0),
frequency_days: 7.0,
confidence: 0.8,
last_seen: i,
};
state.patterns.insert(category_key.clone(), pattern);
// Add Q-values
let q_key = format!("{}|under_budget", category_key);
state.q_values.insert(q_key, 0.5 + (i as f64 * 0.001));
// Add embedding (this will expose the memory leak!)
state.category_embeddings.push((
category_key,
vec![0.1 * (i as f32 % 10.0); 21]
));
}
state.version = tx_count;
let json_string = serde_json::to_string(&state).unwrap();
let state_size = json_string.len();
group.throughput(Throughput::Bytes(state_size as u64));
group.bench_with_input(
BenchmarkId::new("json_serialize", tx_count),
&state,
|b, s| {
b.iter(|| serde_json::to_string(black_box(s)).unwrap());
},
);
group.bench_with_input(
BenchmarkId::new("json_deserialize", tx_count),
&json_string,
|b, json| {
b.iter(|| {
serde_json::from_str::<FinancialLearningState>(black_box(json)).unwrap()
});
},
);
// Benchmark bincode for comparison
let bincode_data = bincode::serialize(&state).unwrap();
group.bench_with_input(
BenchmarkId::new("bincode_serialize", tx_count),
&state,
|b, s| {
b.iter(|| bincode::serialize(black_box(s)).unwrap());
},
);
group.bench_with_input(
BenchmarkId::new("bincode_deserialize", tx_count),
&bincode_data,
|b, data| {
b.iter(|| {
bincode::deserialize::<FinancialLearningState>(black_box(data)).unwrap()
});
},
);
}
group.finish();
}
// ============================================================================
// Memory Footprint Benchmarks
// ============================================================================
fn bench_memory_footprint(c: &mut Criterion) {
let mut group = c.benchmark_group("memory_footprint");
group.bench_function("proof_size_8bit", |b| {
b.iter_custom(|iters| {
let mut total_size = 0;
let start = std::time::Instant::now();
for _ in 0..iters {
let blinding = PedersenCommitment::random_blinding();
let proof = RangeProof::prove(128, 0, 255, &blinding).unwrap();
let size = bincode::serialize(&proof).unwrap().len();
total_size += size;
black_box(size);
}
println!("Average proof size (8-bit): {} bytes", total_size / iters as usize);
start.elapsed()
});
});
group.bench_function("proof_size_32bit", |b| {
b.iter_custom(|iters| {
let mut total_size = 0;
let start = std::time::Instant::now();
for _ in 0..iters {
let blinding = PedersenCommitment::random_blinding();
let proof = RangeProof::prove(50000, 0, 100000, &blinding).unwrap();
let size = bincode::serialize(&proof).unwrap().len();
total_size += size;
black_box(size);
}
println!("Average proof size (32-bit): {} bytes", total_size / iters as usize);
start.elapsed()
});
});
group.bench_function("state_growth_simulation", |b| {
b.iter_custom(|iters| {
let mut state = FinancialLearningState::default();
let start = std::time::Instant::now();
for i in 0..iters {
// Simulate transaction processing (THIS WILL LEAK MEMORY!)
let key = format!("cat_{}", i % 10);
state.category_embeddings.push((key.clone(), vec![0.0; 21]));
// Also add pattern and Q-value
let pattern = SpendingPattern {
pattern_id: format!("pat_{}", i),
category: key.clone(),
avg_amount: 50.0,
frequency_days: 7.0,
confidence: 0.8,
last_seen: i,
};
state.patterns.insert(key.clone(), pattern);
state.q_values.insert(format!("{}|action", key), 0.5);
}
let size = bincode::serialize(&state).unwrap().len();
println!("State size after {} transactions: {} KB", iters, size / 1024);
println!("Embeddings count: {}", state.category_embeddings.len());
start.elapsed()
});
});
group.finish();
}
// ============================================================================
// Regression Tests (detect performance degradation)
// ============================================================================
fn bench_regression_tests(c: &mut Criterion) {
let mut group = c.benchmark_group("regression_tests");
// These benchmarks establish baseline performance
// CI can fail if they regress significantly
group.bench_function("baseline_proof_32bit", |b| {
let blinding = PedersenCommitment::random_blinding();
b.iter(|| {
RangeProof::prove(black_box(50000), 0, black_box(100000), &blinding)
});
});
group.bench_function("baseline_feature_extraction", |b| {
let tx = Transaction {
transaction_id: "tx".to_string(),
account_id: "acc".to_string(),
amount: 50.0,
date: "2024-01-01".to_string(),
name: "Test".to_string(),
merchant_name: Some("Test Merchant".to_string()),
category: vec!["Food".to_string()],
pending: false,
payment_channel: "online".to_string(),
};
b.iter(|| {
let features = extract_features(black_box(&tx));
features.to_embedding()
});
});
group.bench_function("baseline_json_serialize_1k", |b| {
let mut state = FinancialLearningState::default();
for i in 0..1000 {
let key = format!("cat_{}", i % 10);
state.category_embeddings.push((key, vec![0.0; 21]));
}
b.iter(|| {
serde_json::to_string(black_box(&state))
});
});
group.finish();
}
// ============================================================================
// Benchmark Groups
// ============================================================================
criterion_group!(
proof_benches,
bench_proof_generation,
bench_proof_verification,
bench_pedersen_commitment,
bench_financial_proofs,
);
criterion_group!(
learning_benches,
bench_feature_extraction,
bench_lsh_hashing,
bench_q_learning,
bench_transaction_processing,
);
criterion_group!(
overhead_benches,
bench_serialization,
bench_memory_footprint,
);
criterion_group!(
regression_benches,
bench_regression_tests,
);
criterion_main!(
proof_benches,
learning_benches,
overhead_benches,
regression_benches,
);