#![allow(clippy::unwrap_used)] #![allow(missing_docs)]
#![allow(clippy::missing_errors_doc)] use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use fraiseql_core::{
db::{
MySqlProjectionGenerator, PostgresProjectionGenerator, SqliteProjectionGenerator,
types::JsonbValue,
},
runtime::ResultProjector,
};
use serde_json::json;
fn generate_sample_data(field_count: usize, row_count: usize) -> Vec<JsonbValue> {
let mut rows = Vec::with_capacity(row_count);
for row_id in 0..row_count {
let mut obj = serde_json::Map::new();
for field_id in 0..field_count {
let key = format!("field_{}", field_id);
obj.insert(key, json!("value"));
}
obj.insert("id".to_string(), json!(row_id.to_string()));
obj.insert("name".to_string(), json!(format!("User {}", row_id)));
obj.insert("email".to_string(), json!(format!("user{}@example.com", row_id)));
obj.insert("status".to_string(), json!("active"));
obj.insert("created_at".to_string(), json!("2024-01-14T00:00:00Z"));
obj.insert("updated_at".to_string(), json!("2024-01-14T00:00:00Z"));
obj.insert("metadata".to_string(), json!({"score": 100, "tags": ["a", "b", "c"]}));
rows.push(JsonbValue::new(serde_json::Value::Object(obj)));
}
rows
}
fn generate_field_list(count: usize) -> Vec<String> {
(0..count).map(|i| format!("field_{}", i)).collect()
}
fn postgres_projection_generation(c: &mut Criterion) {
let mut group = c.benchmark_group("postgres_projection_generation");
for field_count in &[5, 10, 20, 50] {
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_fields", field_count)),
field_count,
|b, &field_count| {
let generator = PostgresProjectionGenerator::new();
let fields = generate_field_list(field_count);
b.iter(|| generator.generate_projection_sql(black_box(&fields)).unwrap());
},
);
}
group.finish();
}
fn mysql_projection_generation(c: &mut Criterion) {
let mut group = c.benchmark_group("mysql_projection_generation");
for field_count in &[5, 10, 20, 50] {
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_fields", field_count)),
field_count,
|b, &field_count| {
let generator = MySqlProjectionGenerator::new();
let fields = generate_field_list(field_count);
b.iter(|| generator.generate_projection_sql(black_box(&fields)).unwrap());
},
);
}
group.finish();
}
fn sqlite_projection_generation(c: &mut Criterion) {
let mut group = c.benchmark_group("sqlite_projection_generation");
for field_count in &[5, 10, 20, 50] {
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_fields", field_count)),
field_count,
|b, &field_count| {
let generator = SqliteProjectionGenerator::new();
let fields = generate_field_list(field_count);
b.iter(|| generator.generate_projection_sql(black_box(&fields)).unwrap());
},
);
}
group.finish();
}
fn result_projection_field_filtering(c: &mut Criterion) {
let mut group = c.benchmark_group("result_projection_field_filtering");
for row_count in &[10, 100, 1000] {
let field_count = 5;
let fields = generate_field_list(field_count);
let data = generate_sample_data(field_count, *row_count);
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_rows", row_count)),
row_count,
|b, _| {
let projector = ResultProjector::new(fields.clone());
b.iter(|| projector.project_results(black_box(&data), true).unwrap());
},
);
}
group.finish();
}
fn result_projection_large_fieldset(c: &mut Criterion) {
let mut group = c.benchmark_group("result_projection_large_fieldset");
for field_count in &[10, 20, 50] {
let row_count = 100;
let fields = generate_field_list(*field_count);
let data = generate_sample_data(*field_count, row_count);
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_fields", field_count)),
field_count,
|b, _| {
let projector = ResultProjector::new(fields.clone());
b.iter(|| projector.project_results(black_box(&data), true).unwrap());
},
);
}
group.finish();
}
fn add_typename_single_object(c: &mut Criterion) {
let mut group = c.benchmark_group("add_typename_single_object");
for field_count in &[5, 20, 50] {
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_fields", field_count)),
field_count,
|b, &field_count| {
let fields = generate_field_list(field_count);
let data = generate_sample_data(field_count, 1);
let projector = ResultProjector::new(fields);
b.iter(|| projector.add_typename_only(black_box(&data[0]), "User").unwrap());
},
);
}
group.finish();
}
fn add_typename_array(c: &mut Criterion) {
let mut group = c.benchmark_group("add_typename_array");
for row_count in &[10, 100, 1000] {
let field_count = 10;
let fields = generate_field_list(field_count);
let data = generate_sample_data(field_count, *row_count);
let array_data = JsonbValue::new(serde_json::Value::Array(
data.iter().map(|v| v.as_value().clone()).collect(),
));
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_rows", row_count)),
row_count,
|b, _| {
let projector = ResultProjector::new(fields.clone());
b.iter(|| projector.add_typename_only(black_box(&array_data), "User").unwrap());
},
);
}
group.finish();
}
fn complete_pipeline_single_row(c: &mut Criterion) {
let mut group = c.benchmark_group("complete_pipeline_single_row");
for field_count in &[5, 10, 20] {
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_fields", field_count)),
field_count,
|b, &field_count| {
let fields = generate_field_list(field_count);
let data = generate_sample_data(field_count, 1);
let projector = ResultProjector::new(fields);
b.iter(|| {
let _projected = projector.project_results(black_box(&data), false).unwrap();
let with_typename =
projector.add_typename_only(black_box(&data[0]), "User").unwrap();
let _response = ResultProjector::wrap_in_data_envelope(with_typename, "user");
});
},
);
}
group.finish();
}
fn complete_pipeline_array(c: &mut Criterion) {
let mut group = c.benchmark_group("complete_pipeline_array");
for row_count in &[100, 1000, 10000] {
let field_count = 10;
let fields = generate_field_list(field_count);
let data = generate_sample_data(field_count, *row_count);
let projector = ResultProjector::new(fields.clone());
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_rows", row_count)),
row_count,
|b, _| {
b.iter(|| {
let projected = projector.project_results(black_box(&data), true).unwrap();
let _response = ResultProjector::wrap_in_data_envelope(projected, "users");
});
},
);
}
group.finish();
}
fn payload_size_comparison(c: &mut Criterion) {
let mut group = c.benchmark_group("payload_size_comparison");
group.sample_size(10);
for row_count in &[100, 1000, 10000] {
let field_count = 10;
let fields = generate_field_list(field_count);
let data = generate_sample_data(field_count, *row_count);
let projector = ResultProjector::new(fields);
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_rows_unfiltered", row_count)),
row_count,
|b, _| {
b.iter(|| {
let response = ResultProjector::wrap_in_data_envelope(
serde_json::json!(data.iter().map(|v| v.as_value()).collect::<Vec<_>>()),
"users",
);
let size = serde_json::to_string(&response).unwrap().len();
black_box(size)
});
},
);
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_rows_projected", row_count)),
row_count,
|b, _| {
b.iter(|| {
let projected = projector.project_results(black_box(&data), true).unwrap();
let response = ResultProjector::wrap_in_data_envelope(projected, "users");
let size = serde_json::to_string(&response).unwrap().len();
black_box(size)
});
},
);
}
group.finish();
}
criterion_group!(
benches,
postgres_projection_generation,
mysql_projection_generation,
sqlite_projection_generation,
result_projection_field_filtering,
result_projection_large_fieldset,
add_typename_single_object,
add_typename_array,
complete_pipeline_single_row,
complete_pipeline_array,
payload_size_comparison,
);
criterion_main!(benches);