480 lines
15 KiB
Rust
480 lines
15 KiB
Rust
//! Performance benchmarks for rtx-data-validation
|
|
//!
|
|
//! This module contains comprehensive benchmarks for all validation operations
|
|
//! to measure and track performance characteristics.
|
|
|
|
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
|
|
use std::collections::HashMap;
|
|
use std::time::Duration;
|
|
use tokio::runtime::Runtime;
|
|
|
|
use rtx_data_validation::*;
|
|
|
|
/// Helper function to create sample data for benchmarks
|
|
fn create_benchmark_record(id: usize) -> DataRecord {
|
|
let mut fields = HashMap::new();
|
|
fields.insert("id".to_string(), DataValue::Int(id as i64));
|
|
fields.insert(
|
|
"name".to_string(),
|
|
DataValue::String(format!("User {}", id)),
|
|
);
|
|
fields.insert(
|
|
"email".to_string(),
|
|
DataValue::String(format!("user{}@example.com", id)),
|
|
);
|
|
fields.insert("age".to_string(), DataValue::Int(25 + (id % 40) as i64));
|
|
fields.insert(
|
|
"score".to_string(),
|
|
DataValue::Float(50.0 + (id % 50) as f64),
|
|
);
|
|
fields.insert("active".to_string(), DataValue::Bool(id % 2 == 0));
|
|
|
|
DataRecord {
|
|
id: format!("record_{}", id),
|
|
timestamp: chrono::Utc::now(),
|
|
fields,
|
|
metadata: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Create multiple benchmark records
|
|
fn create_benchmark_records(count: usize) -> Vec<DataRecord> {
|
|
(0..count).map(create_benchmark_record).collect()
|
|
}
|
|
|
|
/// Benchmark basic validation engine performance
|
|
fn benchmark_validation_engine(c: &mut Criterion) {
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
// Create engine with various rule configurations
|
|
let simple_engine = ValidationEngine::builder()
|
|
.add_rule(ValidationRule::not_null("name"))
|
|
.build()
|
|
.expect("Failed to build simple engine");
|
|
|
|
let complex_engine = ValidationEngine::builder()
|
|
.with_statistical_validation(true)
|
|
.add_rule(ValidationRule::not_null("name"))
|
|
.add_rule(ValidationRule::range("age", 0.0, 120.0))
|
|
.add_rule(ValidationRule::email("email"))
|
|
.add_rule(ValidationRule::range("score", 0.0, 100.0))
|
|
.add_rule(ValidationRule::length("name", 2, 100))
|
|
.build()
|
|
.expect("Failed to build complex engine");
|
|
|
|
let test_record = create_benchmark_record(1);
|
|
|
|
// Benchmark single record validation
|
|
c.bench_function("validation_single_simple", |b| {
|
|
b.to_async(&rt)
|
|
.iter(|| async { black_box(simple_engine.validate(&test_record).await.unwrap()) })
|
|
});
|
|
|
|
c.bench_function("validation_single_complex", |b| {
|
|
b.to_async(&rt)
|
|
.iter(|| async { black_box(complex_engine.validate(&test_record).await.unwrap()) })
|
|
});
|
|
|
|
// Benchmark batch validation with different sizes
|
|
for size in [10, 100, 1000].iter() {
|
|
let records = create_benchmark_records(*size);
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("validation_batch_simple", size),
|
|
&records,
|
|
|b, records| {
|
|
b.to_async(&rt).iter(|| async {
|
|
black_box(simple_engine.validate_batch(records).await.unwrap())
|
|
})
|
|
},
|
|
);
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("validation_batch_complex", size),
|
|
&records,
|
|
|b, records| {
|
|
b.to_async(&rt).iter(|| async {
|
|
black_box(complex_engine.validate_batch(records).await.unwrap())
|
|
})
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Benchmark validation rules performance
|
|
fn benchmark_validation_rules(c: &mut Criterion) {
|
|
let test_values = vec![
|
|
DataValue::String("[email protected]".to_string()),
|
|
DataValue::Int(25),
|
|
DataValue::Float(85.5),
|
|
DataValue::String("Valid Name".to_string()),
|
|
];
|
|
|
|
// Benchmark individual rule types
|
|
let not_null_rule = ValidationRule::not_null("test");
|
|
let range_rule = ValidationRule::range("test", 0.0, 100.0);
|
|
let email_rule = ValidationRule::email("test");
|
|
let length_rule = ValidationRule::length("test", 2, 100);
|
|
|
|
c.bench_function("rule_not_null", |b| {
|
|
b.iter(|| {
|
|
for value in &test_values {
|
|
black_box(not_null_rule.validate(value));
|
|
}
|
|
})
|
|
});
|
|
|
|
c.bench_function("rule_range", |b| {
|
|
b.iter(|| {
|
|
for value in &test_values {
|
|
black_box(range_rule.validate(value));
|
|
}
|
|
})
|
|
});
|
|
|
|
c.bench_function("rule_email", |b| {
|
|
b.iter(|| {
|
|
black_box(email_rule.validate(&test_values[0]));
|
|
})
|
|
});
|
|
|
|
c.bench_function("rule_length", |b| {
|
|
b.iter(|| {
|
|
black_box(length_rule.validate(&test_values[3]));
|
|
})
|
|
});
|
|
|
|
// Benchmark cross-field validation
|
|
let cross_field_rule = ValidationRule::cross_field(
|
|
"field1",
|
|
"field2",
|
|
crate::rules::CrossFieldOperation::Greater,
|
|
);
|
|
|
|
let mut context = HashMap::new();
|
|
context.insert("field2".to_string(), DataValue::Int(20));
|
|
|
|
c.bench_function("rule_cross_field", |b| {
|
|
b.iter(|| {
|
|
black_box(cross_field_rule.validate_with_context(&DataValue::Int(30), &context));
|
|
})
|
|
});
|
|
}
|
|
|
|
/// Benchmark statistical profiling performance
|
|
fn benchmark_statistical_profiling(c: &mut Criterion) {
|
|
// Test numerical statistics with different data sizes
|
|
for size in [100, 1000, 10000].iter() {
|
|
let values: Vec<f64> = (0..*size).map(|i| (i as f64) + (i as f64 * 0.1)).collect();
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("numerical_statistics", size),
|
|
&values,
|
|
|b, values| b.iter(|| black_box(NumericalStatistics::from_values(values).unwrap())),
|
|
);
|
|
}
|
|
|
|
// Benchmark data profile generation
|
|
for size in [100, 1000].iter() {
|
|
let records = create_benchmark_records(*size);
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("data_profile_generation", size),
|
|
&records,
|
|
|b, records| {
|
|
b.iter(|| {
|
|
let mut profile = DataProfile::new();
|
|
for record in records {
|
|
profile.add_record(&record.fields).unwrap();
|
|
}
|
|
profile.finalize().unwrap();
|
|
black_box(profile)
|
|
})
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Benchmark quality scoring performance
|
|
fn benchmark_quality_scoring(c: &mut Criterion) {
|
|
for size in [100, 1000, 5000].iter() {
|
|
let records = create_benchmark_records(*size);
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("quality_score_calculation", size),
|
|
&records,
|
|
|b, records| {
|
|
b.iter(|| black_box(QualityScore::calculate_for_records(records).unwrap()))
|
|
},
|
|
);
|
|
}
|
|
|
|
// Benchmark individual quality dimensions
|
|
let records = create_benchmark_records(1000);
|
|
|
|
c.bench_function("completeness_scoring", |b| {
|
|
b.iter(|| {
|
|
// Simplified completeness calculation
|
|
let total_fields = records.len() * 6; // 6 fields per record
|
|
let non_null_fields = records
|
|
.iter()
|
|
.flat_map(|r| r.fields.values())
|
|
.filter(|v| !v.is_null())
|
|
.count();
|
|
black_box(non_null_fields as f64 / total_fields as f64)
|
|
})
|
|
});
|
|
}
|
|
|
|
/// Benchmark anomaly detection performance
|
|
fn benchmark_anomaly_detection(c: &mut Criterion) {
|
|
// Prepare test data
|
|
let normal_data: Vec<f64> = (0..1000)
|
|
.map(|i| i as f64 + rand::random::<f64>() * 10.0)
|
|
.collect();
|
|
let test_values = vec![500.0, 5000.0]; // Normal and anomalous
|
|
|
|
// Benchmark Z-score detection
|
|
let z_score_detector = ZScoreDetector::new(3.0);
|
|
c.bench_function("z_score_detection", |b| {
|
|
b.iter(|| {
|
|
for &value in &test_values {
|
|
black_box(
|
|
z_score_detector
|
|
.detect("test", value, &normal_data)
|
|
.unwrap(),
|
|
);
|
|
}
|
|
})
|
|
});
|
|
|
|
// Benchmark IQR detection
|
|
let iqr_detector = IQRDetector::new(1.5);
|
|
c.bench_function("iqr_detection", |b| {
|
|
b.iter(|| {
|
|
for &value in &test_values {
|
|
black_box(iqr_detector.detect("test", value, &normal_data).unwrap());
|
|
}
|
|
})
|
|
});
|
|
|
|
// Benchmark Isolation Forest (simplified)
|
|
let mut isolation_forest = IsolationForestDetector::new(10, 100, 0.1);
|
|
let training_data: Vec<Vec<f64>> = (0..100).map(|i| vec![i as f64]).collect();
|
|
isolation_forest.train(&training_data).unwrap();
|
|
|
|
c.bench_function("isolation_forest_score", |b| {
|
|
b.iter(|| black_box(isolation_forest.score_multivariate(&[500.0]).unwrap()))
|
|
});
|
|
}
|
|
|
|
/// Benchmark schema operations
|
|
fn benchmark_schema_operations(c: &mut Criterion) {
|
|
let mut schema_manager = SchemaManager::new();
|
|
|
|
// Benchmark schema inference with different data sizes
|
|
for size in [100, 1000].iter() {
|
|
let records = create_benchmark_records(*size);
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("schema_inference", size),
|
|
&records,
|
|
|b, records| {
|
|
b.iter(|| {
|
|
let mut inference = SchemaInference::new();
|
|
black_box(
|
|
inference
|
|
.infer_schema("benchmark_schema".to_string(), records)
|
|
.unwrap(),
|
|
)
|
|
})
|
|
},
|
|
);
|
|
}
|
|
|
|
// Benchmark schema validation
|
|
let sample_records = create_benchmark_records(10);
|
|
let schema = schema_manager
|
|
.infer_schema("test_schema".to_string(), &sample_records)
|
|
.unwrap();
|
|
schema_manager.register_schema(schema).unwrap();
|
|
|
|
let test_record = create_benchmark_record(1);
|
|
c.bench_function("schema_validation", |b| {
|
|
b.iter(|| black_box(schema_manager.validate_record(&test_record).unwrap()))
|
|
});
|
|
}
|
|
|
|
/// Benchmark pipeline performance
|
|
fn benchmark_pipeline_performance(c: &mut Criterion) {
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
// Create pipeline components
|
|
let engine = std::sync::Arc::new(
|
|
ValidationEngine::builder()
|
|
.add_rule(ValidationRule::not_null("name"))
|
|
.add_rule(ValidationRule::range("age", 0.0, 120.0))
|
|
.build()
|
|
.expect("Failed to build pipeline engine"),
|
|
);
|
|
|
|
// Benchmark streaming validation
|
|
let stream_config = crate::pipeline::StreamConfig::default();
|
|
let streaming_validator =
|
|
crate::pipeline::StreamingValidator::new(engine.clone(), stream_config);
|
|
|
|
c.bench_function("streaming_add_record", |b| {
|
|
b.to_async(&rt).iter(|| async {
|
|
let record = create_benchmark_record(1);
|
|
black_box(streaming_validator.add_record(record).await.unwrap())
|
|
})
|
|
});
|
|
|
|
// Benchmark batch validation
|
|
let batch_config = crate::pipeline::BatchConfig::default();
|
|
let batch_validator = crate::pipeline::BatchValidator::new(engine, batch_config);
|
|
|
|
for size in [100, 1000].iter() {
|
|
let batch_records = create_benchmark_records(*size);
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("batch_validation", size),
|
|
&batch_records,
|
|
|b, records| {
|
|
b.to_async(&rt).iter(|| async {
|
|
black_box(
|
|
batch_validator
|
|
.process_batch(records.clone())
|
|
.await
|
|
.unwrap(),
|
|
)
|
|
})
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Benchmark metrics collection
|
|
fn benchmark_metrics_collection(c: &mut Criterion) {
|
|
let mut metrics = ValidationMetrics::new();
|
|
|
|
c.bench_function("metrics_record_validation", |b| {
|
|
b.iter(|| black_box(metrics.record_validation(Duration::from_millis(100), true)))
|
|
});
|
|
|
|
c.bench_function("metrics_record_error", |b| {
|
|
b.iter(|| black_box(metrics.record_error("test_error", "warning")))
|
|
});
|
|
|
|
c.bench_function("metrics_update_resources", |b| {
|
|
b.iter(|| black_box(metrics.update_resource_metrics(75.0, 1_000_000, 10)))
|
|
});
|
|
|
|
c.bench_function("metrics_calculate_throughput", |b| {
|
|
b.iter(|| black_box(metrics.calculate_throughput(60.0)))
|
|
});
|
|
|
|
// Benchmark metrics with large history
|
|
let mut large_metrics = ValidationMetrics::new();
|
|
for i in 0..10000 {
|
|
large_metrics.record_validation(Duration::from_millis(50 + i % 100), i % 10 != 0);
|
|
}
|
|
|
|
c.bench_function("metrics_summary_large", |b| {
|
|
b.iter(|| black_box(large_metrics.summary()))
|
|
});
|
|
}
|
|
|
|
/// Benchmark throughput with different configurations
|
|
fn benchmark_throughput_scaling(c: &mut Criterion) {
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
// Test different engine configurations
|
|
let engines = vec![
|
|
(
|
|
"minimal",
|
|
ValidationEngine::builder()
|
|
.add_rule(ValidationRule::not_null("name"))
|
|
.build()
|
|
.unwrap(),
|
|
),
|
|
(
|
|
"standard",
|
|
ValidationEngine::builder()
|
|
.add_rule(ValidationRule::not_null("name"))
|
|
.add_rule(ValidationRule::range("age", 0.0, 120.0))
|
|
.add_rule(ValidationRule::email("email"))
|
|
.build()
|
|
.unwrap(),
|
|
),
|
|
(
|
|
"comprehensive",
|
|
ValidationEngine::builder()
|
|
.with_statistical_validation(true)
|
|
.add_rule(ValidationRule::not_null("name"))
|
|
.add_rule(ValidationRule::range("age", 0.0, 120.0))
|
|
.add_rule(ValidationRule::email("email"))
|
|
.add_rule(ValidationRule::range("score", 0.0, 100.0))
|
|
.add_rule(ValidationRule::length("name", 2, 100))
|
|
.build()
|
|
.unwrap(),
|
|
),
|
|
];
|
|
|
|
// Test different record sizes
|
|
for (engine_name, engine) in engines {
|
|
for size in [100, 1000, 10000].iter() {
|
|
let records = create_benchmark_records(*size);
|
|
|
|
let mut group = c.benchmark_group(format!("throughput_{}_{}", engine_name, size));
|
|
group.throughput(Throughput::Elements(*size as u64));
|
|
|
|
group.bench_function("batch", |b| {
|
|
b.to_async(&rt)
|
|
.iter(|| async { black_box(engine.validate_batch(&records).await.unwrap()) })
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Benchmark memory efficiency
|
|
fn benchmark_memory_efficiency(c: &mut Criterion) {
|
|
// Test memory usage with different data sizes
|
|
for size in [1000, 10000, 100000].iter() {
|
|
let records = create_benchmark_records(*size);
|
|
|
|
c.bench_with_input(
|
|
BenchmarkId::new("memory_profile_generation", size),
|
|
&records,
|
|
|b, records| {
|
|
b.iter(|| {
|
|
let mut profile = DataProfile::new();
|
|
for record in records {
|
|
profile.add_record(&record.fields).unwrap();
|
|
}
|
|
// Measure peak memory usage during profile generation
|
|
black_box(profile)
|
|
})
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
benchmark_validation_engine,
|
|
benchmark_validation_rules,
|
|
benchmark_statistical_profiling,
|
|
benchmark_quality_scoring,
|
|
benchmark_anomaly_detection,
|
|
benchmark_schema_operations,
|
|
benchmark_pipeline_performance,
|
|
benchmark_metrics_collection,
|
|
benchmark_throughput_scaling,
|
|
benchmark_memory_efficiency
|
|
);
|
|
|
|
criterion_main!(benches);
|