use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use rust_ef::db_context::{DbContext, DbContextOptionsBuilder};
use rust_ef::prelude::*;
use rust_ef_sqlite::DbContextOptionsBuilderExt as _;
use tokio::runtime::Runtime;
#[derive(Debug, Clone, EntityType)]
#[table("bench_detect_widgets")]
struct BenchDetectWidget {
#[primary_key]
#[auto_increment]
id: i32,
#[required]
#[max_length(100)]
name: String,
value: f64,
}
async fn seeded_ctx(n: usize) -> DbContext {
let mut builder = DbContextOptionsBuilder::new();
builder.use_sqlite_in_memory();
let options = builder.build();
let mut ctx = DbContext::from_options(&options).expect("DbContext");
ctx.set::<BenchDetectWidget>();
ctx.ensure_created().await.expect("ensure_created");
for i in 0..n {
ctx.set::<BenchDetectWidget>().add(BenchDetectWidget {
id: 0,
name: format!("widget-{i}"),
value: i as f64,
});
}
ctx.save_changes().await.expect("seed save");
ctx
}
async fn detect_changes_half_modified(n: usize) {
let mut ctx = seeded_ctx(n).await;
let items = ctx
.set::<BenchDetectWidget>()
.query()
.to_list()
.await
.expect("load");
ctx.set::<BenchDetectWidget>().clear_entries();
for item in items {
ctx.set::<BenchDetectWidget>().attach(item);
}
let half = n / 2;
for (i, entry) in ctx
.set::<BenchDetectWidget>()
.tracked_entries_mut()
.enumerate()
{
if i < half {
entry.value += 1.0;
}
}
ctx.set::<BenchDetectWidget>().detect_changes();
}
fn bench_detect_changes(c: &mut Criterion) {
let rt = Runtime::new().expect("tokio runtime");
rt.block_on(detect_changes_half_modified(10));
let mut group = c.benchmark_group("detect_changes");
group.sample_size(20);
for n in [500usize, 1000] {
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
b.to_async(&rt)
.iter(|| async move { detect_changes_half_modified(n).await });
});
}
group.finish();
}
criterion_group!(benches, bench_detect_changes);
criterion_main!(benches);