From 29aa786fd304fc321dfb2428b098ac91362be1dc Mon Sep 17 00:00:00 2001 From: Handy-caT <37216852+Handy-caT@users.noreply.github.com> Date: Mon, 20 Apr 2026 19:52:31 +0300 Subject: [PATCH 1/3] add benches --- Cargo.toml | 5 + benches/cases/full_featured.rs | 395 ++++++++++++++++++++++++++++++ benches/cases/mod.rs | 4 + benches/cases/non_unique_index.rs | 236 ++++++++++++++++++ benches/cases/simple.rs | 204 +++++++++++++++ benches/cases/unique_index.rs | 237 ++++++++++++++++++ benches/common/config.rs | 9 + benches/common/mod.rs | 69 ++++++ benches/worktable_benchmarks.rs | 11 + 9 files changed, 1170 insertions(+) create mode 100644 benches/cases/full_featured.rs create mode 100644 benches/cases/mod.rs create mode 100644 benches/cases/non_unique_index.rs create mode 100644 benches/cases/simple.rs create mode 100644 benches/cases/unique_index.rs create mode 100644 benches/common/config.rs create mode 100644 benches/common/mod.rs create mode 100644 benches/worktable_benchmarks.rs diff --git a/Cargo.toml b/Cargo.toml index 086b78a..36fa569 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,4 +50,9 @@ worktable_codegen = { path = "codegen", version = "=0.9.0-beta0.1.3" } [dev-dependencies] chrono = "0.4.43" +criterion = { version = "0.5", features = ["async_tokio"] } rand = "0.9.1" + +[[bench]] +name = "worktable_benchmarks" +harness = false diff --git a/benches/cases/full_featured.rs b/benches/cases/full_featured.rs new file mode 100644 index 0000000..ac990f3 --- /dev/null +++ b/benches/cases/full_featured.rs @@ -0,0 +1,395 @@ +use criterion::{black_box, criterion_group, Criterion, BatchSize, BenchmarkId, Throughput}; +use std::sync::Arc; +use tokio::runtime::Runtime; +use worktable::prelude::SelectQueryExecutor; + +use crate::common::*; + +fn insert(c: &mut Criterion) { + let table = FullFeaturedWorkTable::default(); + + c.bench_function("full_featured_insert", |b| { + b.iter_batched( + || (), + |_| { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: fastrand::i64(..), + val1: fastrand::u64(..), + another: format!("another_{}", fastrand::u64(..)), + something: fastrand::u64(..), + }; + table.insert(black_box(row)) + }, + BatchSize::SmallInput, + ) + }); +} + +fn select_by_pk(c: &mut Criterion) { + let table = FullFeaturedWorkTable::default(); + let pks: Vec<_> = (0..1000) + .map(|_| { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: fastrand::i64(..), + val1: fastrand::u64(..), + another: format!("another_{}", fastrand::u64(..)), + something: fastrand::u64(..), + }; + table.insert(row).unwrap() + }) + .collect(); + + c.bench_function("full_featured_select_by_pk", |b| { + b.iter(|| { + let pk = pks[fastrand::usize(0..pks.len())].clone(); + black_box(table.select(pk)) + }) + }); +} + +fn select_by_unique_index(c: &mut Criterion) { + let table = FullFeaturedWorkTable::default(); + + for i in 0..1000u64 { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: i as i64, + val1: i, + another: format!("another_{}", i), + something: i, + }; + table.insert(row).unwrap(); + } + + c.bench_function("full_featured_select_by_val1", |b| { + b.iter(|| { + let val1 = fastrand::u64(0..1000); + black_box(table.select_by_val1(val1)) + }) + }); +} + +fn select_by_non_unique_index(c: &mut Criterion) { + let table = FullFeaturedWorkTable::default(); + + for i in 0..1000u64 { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: i as i64, + val1: i, + another: format!("cat_{}", i % 10), + something: i, + }; + table.insert(row).unwrap(); + } + + c.bench_function("full_featured_select_by_another", |b| { + b.iter(|| { + let cat = format!("cat_{}", fastrand::u64(0..10)); + black_box(table.select_by_another(cat).execute()) + }) + }); +} + +fn update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + let pks: Vec<_> = rt.block_on(async { + let mut pks = Vec::new(); + for i in 0..100u64 { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: i as i64, + val1: i, + another: format!("another_{}", i), + something: i, + }; + pks.push(table.insert(row).unwrap()); + } + pks + }); + + c.bench_function("full_featured_update", |b| { + b.to_async(&rt).iter(|| async { + let idx = fastrand::usize(0..pks.len()); + let pk = pks[idx].clone(); + let row = FullFeaturedRow { + id: pk.into(), + val: fastrand::i64(..), + val1: fastrand::u64(..), + another: format!("updated_{}", fastrand::u64(..)), + something: fastrand::u64(..), + }; + black_box(table.update(row).await) + }) + }); +} + +fn update_by_pk_query(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + rt.block_on(async { + for i in 0..100u64 { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: i as i64, + val1: i, + another: format!("another_{}", i), + something: i, + }; + table.insert(row).unwrap(); + } + }); + + c.bench_function("full_featured_update_another_by_id", |b| { + b.to_async(&rt).iter(|| async { + let id = fastrand::u64(0..100); + let query = AnotherByIdQuery { + another: format!("upd_{}", fastrand::u64(..)), + }; + black_box(table.update_another_by_id(query, id).await) + }) + }); +} + +fn update_by_unique_index_query(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + rt.block_on(async { + for i in 0..100u64 { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: i as i64, + val1: i, + another: format!("another_{}", i), + something: i, + }; + table.insert(row).unwrap(); + } + }); + + c.bench_function("full_featured_update_another_by_val1", |b| { + b.to_async(&rt).iter(|| async { + let val1 = fastrand::u64(0..100); + let query = AnotherByVal1Query { + another: format!("upd_{}", fastrand::u64(..)), + }; + black_box(table.update_another_by_val_1(query, val1).await) + }) + }); +} + +fn in_place_update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + let pk: u64 = { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: 0, + val1: 0, + another: "test".to_string(), + something: 0, + }; + table.insert(row).unwrap().into() + }; + + c.bench_function("full_featured_in_place_update_val", |b| { + b.to_async(&rt).iter(|| async { + table + .update_val_by_id_in_place(|val| *val += 1, black_box(pk)) + .await + }) + }); +} + +fn delete(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + c.bench_function("full_featured_delete", |b| { + b.iter_batched( + || { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: fastrand::i64(..), + val1: fastrand::u64(..), + another: format!("temp_{}", fastrand::u64(..)), + something: fastrand::u64(..), + }; + table.insert(row).unwrap() + }, + |pk: FullFeaturedPrimaryKey| { + rt.block_on(async { + table.delete(black_box(pk)).await.unwrap() + }) + }, + BatchSize::SmallInput, + ) + }); +} + +fn delete_by_index_query(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + c.bench_function("full_featured_delete_by_another", |b| { + b.iter_batched( + || { + let another = format!("del_{}", fastrand::u64(..)); + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: fastrand::i64(..), + val1: fastrand::u64(..), + another: another.clone(), + something: fastrand::u64(..), + }; + table.insert(row).unwrap(); + another + }, + |another: String| { + rt.block_on(async { + table.delete_by_another(another).await.unwrap() + }) + }, + BatchSize::SmallInput, + ) + }); +} + +fn upsert_insert(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + c.bench_function("full_featured_upsert_insert", |b| { + b.to_async(&rt).iter(|| async { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: fastrand::i64(..), + val1: fastrand::u64(..), + another: format!("another_{}", fastrand::u64(..)), + something: fastrand::u64(..), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn upsert_update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(FullFeaturedWorkTable::default()); + + rt.block_on(async { + for i in 0..50u64 { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: i as i64, + val1: i, + another: format!("another_{}", i), + something: i, + }; + table.upsert(row).await.unwrap(); + } + }); + + c.bench_function("full_featured_upsert_update", |b| { + b.to_async(&rt).iter(|| async { + let id = fastrand::u64(0..50); + let row = FullFeaturedRow { + id, + val: fastrand::i64(..), + val1: id, + another: format!("upserted_{}", fastrand::u64(..)), + something: fastrand::u64(..), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn batch_insert(c: &mut Criterion) { + let mut group = c.benchmark_group("full_featured_batch_insert"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter_batched( + || FullFeaturedWorkTable::default(), + |table: FullFeaturedWorkTable| { + for i in 0..size { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: i as i64, + val1: i as u64, + another: format!("another_{}", i), + something: i as u64, + }; + table.insert(black_box(row)).unwrap(); + } + black_box(table) + }, + BatchSize::SmallInput, + ) + }); + } + + group.finish(); +} + +fn batch_select_pk(c: &mut Criterion) { + let mut group = c.benchmark_group("full_featured_batch_select_pk"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + let table = FullFeaturedWorkTable::default(); + let pks: Vec<_> = (0..size) + .map(|_| { + let row = FullFeaturedRow { + id: table.get_next_pk().into(), + val: fastrand::i64(..), + val1: fastrand::u64(..), + another: format!("another_{}", fastrand::u64(..)), + something: fastrand::u64(..), + }; + table.insert(row).unwrap() + }) + .collect(); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter(|| { + for pk in &pks { + black_box(table.select(pk.clone())); + } + }) + }); + } + + group.finish(); +} + +criterion_group! { + name = full_featured_benchmarks; + config = crate::common::config::configure_criterion(); + targets = + insert, + select_by_pk, + select_by_unique_index, + select_by_non_unique_index, + update, + update_by_pk_query, + update_by_unique_index_query, + in_place_update, + delete, + delete_by_index_query, + upsert_insert, + upsert_update, + batch_insert, + batch_select_pk, +} \ No newline at end of file diff --git a/benches/cases/mod.rs b/benches/cases/mod.rs new file mode 100644 index 0000000..eb40711 --- /dev/null +++ b/benches/cases/mod.rs @@ -0,0 +1,4 @@ +pub mod simple; +pub mod unique_index; +pub mod non_unique_index; +pub mod full_featured; \ No newline at end of file diff --git a/benches/cases/non_unique_index.rs b/benches/cases/non_unique_index.rs new file mode 100644 index 0000000..25586b4 --- /dev/null +++ b/benches/cases/non_unique_index.rs @@ -0,0 +1,236 @@ +use criterion::{BatchSize, BenchmarkId, Criterion, Throughput, black_box, criterion_group}; +use std::sync::Arc; +use tokio::runtime::Runtime; +use worktable::prelude::SelectQueryExecutor; + +use crate::common::*; + +fn insert(c: &mut Criterion) { + let table = NonUniqueIndexWorkTable::default(); + + c.bench_function("non_unique_index_insert", |b| { + b.iter_batched( + || (), + |_| { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + category: fastrand::u64(0..10), + }; + table.insert(black_box(row)) + }, + BatchSize::SmallInput, + ) + }); +} + +fn select_by_pk(c: &mut Criterion) { + let table = NonUniqueIndexWorkTable::default(); + let pks: Vec<_> = (0..1000) + .map(|_| { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + category: fastrand::u64(0..10), + }; + table.insert(row).unwrap() + }) + .collect(); + + c.bench_function("non_unique_index_select_by_pk", |b| { + b.iter(|| { + let pk = pks[fastrand::usize(0..pks.len())].clone(); + black_box(table.select(pk)) + }) + }); +} + +fn select_by_non_unique_index(c: &mut Criterion) { + let table = NonUniqueIndexWorkTable::default(); + + for i in 0..1000u64 { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + category: i % 10, + }; + table.insert(row).unwrap(); + } + + c.bench_function("non_unique_index_select_by_category", |b| { + b.iter(|| { + let cat = fastrand::u64(0..10); + black_box(table.select_by_category(cat).execute()) + }) + }); +} + +fn update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(NonUniqueIndexWorkTable::default()); + + let pks: Vec<_> = rt.block_on(async { + let mut pks = Vec::new(); + for i in 0..100u64 { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: i, + category: i % 10, + }; + pks.push(table.insert(row).unwrap()); + } + pks + }); + + c.bench_function("non_unique_index_update", |b| { + b.to_async(&rt).iter(|| async { + let idx = fastrand::usize(0..pks.len()); + let pk = pks[idx].clone(); + let row = NonUniqueIndexRow { + id: pk.into(), + value: fastrand::u64(..), + category: fastrand::u64(0..10), + }; + black_box(table.update(row).await) + }) + }); +} + +fn delete(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(NonUniqueIndexWorkTable::default()); + + c.bench_function("non_unique_index_delete", |b| { + b.iter_batched( + || { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + category: fastrand::u64(0..10), + }; + table.insert(row).unwrap() + }, + |pk: NonUniqueIndexPrimaryKey| { + rt.block_on(async { table.delete(black_box(pk)).await.unwrap() }) + }, + BatchSize::SmallInput, + ) + }); +} + +fn upsert_insert(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(NonUniqueIndexWorkTable::default()); + + c.bench_function("non_unique_index_upsert_insert", |b| { + b.to_async(&rt).iter(|| async { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + category: fastrand::u64(0..10), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn upsert_update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(NonUniqueIndexWorkTable::default()); + + rt.block_on(async { + for i in 0..50u64 { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: i, + category: i % 10, + }; + table.upsert(row).await.unwrap(); + } + }); + + c.bench_function("non_unique_index_upsert_update", |b| { + b.to_async(&rt).iter(|| async { + let id = fastrand::u64(0..50); + let row = NonUniqueIndexRow { + id, + value: fastrand::u64(..), + category: fastrand::u64(0..10), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn batch_insert(c: &mut Criterion) { + let mut group = c.benchmark_group("non_unique_index_batch_insert"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter_batched( + || NonUniqueIndexWorkTable::default(), + |table: NonUniqueIndexWorkTable| { + for i in 0..size { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: i as u64, + category: (i % 10) as u64, + }; + table.insert(black_box(row)).unwrap(); + } + black_box(table) + }, + BatchSize::SmallInput, + ) + }); + } + + group.finish(); +} + +fn batch_select_pk(c: &mut Criterion) { + let mut group = c.benchmark_group("non_unique_index_batch_select_pk"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + let table = NonUniqueIndexWorkTable::default(); + let pks: Vec<_> = (0..size) + .map(|_| { + let row = NonUniqueIndexRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + category: fastrand::u64(0..10), + }; + table.insert(row).unwrap() + }) + .collect(); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter(|| { + for pk in &pks { + black_box(table.select(pk.clone())); + } + }) + }); + } + + group.finish(); +} + +criterion_group! { + name = non_unique_index_benchmarks; + config = config::configure_criterion(); + targets = + insert, + select_by_pk, + select_by_non_unique_index, + update, + delete, + upsert_insert, + upsert_update, + batch_insert, + batch_select_pk, +} diff --git a/benches/cases/simple.rs b/benches/cases/simple.rs new file mode 100644 index 0000000..fef9693 --- /dev/null +++ b/benches/cases/simple.rs @@ -0,0 +1,204 @@ +use criterion::{BatchSize, BenchmarkId, Criterion, Throughput, black_box, criterion_group}; +use std::sync::Arc; +use tokio::runtime::Runtime; + +use crate::common::*; + +fn insert(c: &mut Criterion) { + let table = SimpleWorkTable::default(); + + c.bench_function("simple_insert", |b| { + b.iter_batched( + || (), + |_| { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + }; + table.insert(black_box(row)) + }, + BatchSize::SmallInput, + ) + }); +} + +fn select_by_pk(c: &mut Criterion) { + let table = SimpleWorkTable::default(); + let pks: Vec<_> = (0..1000) + .map(|_| { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + }; + table.insert(row).unwrap() + }) + .collect(); + + c.bench_function("simple_select_by_pk", |b| { + b.iter(|| { + let pk = pks[fastrand::usize(0..pks.len())].clone(); + black_box(table.select(pk)) + }) + }); +} + +fn update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(SimpleWorkTable::default()); + + let pks: Vec<_> = rt.block_on(async { + let mut pks = Vec::new(); + for i in 0..100u64 { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: i, + }; + pks.push(table.insert(row).unwrap()); + } + pks + }); + + c.bench_function("simple_update", |b| { + b.to_async(&rt).iter(|| async { + let idx = fastrand::usize(0..pks.len()); + let pk = pks[idx].clone(); + let row = SimpleRow { + id: pk.into(), + value: fastrand::u64(..), + }; + black_box(table.update(row).await) + }) + }); +} + +fn delete(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(SimpleWorkTable::default()); + + c.bench_function("simple_delete", |b| { + b.iter_batched( + || { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + }; + table.insert(row).unwrap() + }, + |pk: SimplePrimaryKey| { + rt.block_on(async { table.delete(black_box(pk)).await.unwrap() }) + }, + BatchSize::SmallInput, + ) + }); +} + +fn upsert_insert(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(SimpleWorkTable::default()); + + c.bench_function("simple_upsert_insert", |b| { + b.to_async(&rt).iter(|| async { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn upsert_update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(SimpleWorkTable::default()); + + rt.block_on(async { + for i in 0..50u64 { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: i, + }; + table.upsert(row).await.unwrap(); + } + }); + + c.bench_function("simple_upsert_update", |b| { + b.to_async(&rt).iter(|| async { + let id = fastrand::u64(0..50); + let row = SimpleRow { + id, + value: fastrand::u64(..), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn batch_insert(c: &mut Criterion) { + let mut group = c.benchmark_group("simple_batch_insert"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter_batched( + || SimpleWorkTable::default(), + |table: SimpleWorkTable| { + for i in 0..size { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: i as u64, + }; + table.insert(black_box(row)).unwrap(); + } + black_box(table) + }, + BatchSize::SmallInput, + ) + }); + } + + group.finish(); +} + +fn batch_select_pk(c: &mut Criterion) { + let mut group = c.benchmark_group("simple_batch_select_pk"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + let table = SimpleWorkTable::default(); + let pks: Vec<_> = (0..size) + .map(|_| { + let row = SimpleRow { + id: table.get_next_pk().into(), + value: fastrand::u64(..), + }; + table.insert(row).unwrap() + }) + .collect(); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter(|| { + for pk in &pks { + black_box(table.select(pk.clone())); + } + }) + }); + } + + group.finish(); +} + +criterion_group! { + name = simple_benchmarks; + config = config::configure_criterion(); + targets = + insert, + select_by_pk, + update, + delete, + upsert_insert, + upsert_update, + batch_insert, + batch_select_pk, +} diff --git a/benches/cases/unique_index.rs b/benches/cases/unique_index.rs new file mode 100644 index 0000000..457b94d --- /dev/null +++ b/benches/cases/unique_index.rs @@ -0,0 +1,237 @@ +use criterion::{black_box, criterion_group, Criterion, BatchSize, BenchmarkId, Throughput}; +use std::sync::Arc; +use tokio::runtime::Runtime; + +use crate::common::*; + +fn insert(c: &mut Criterion) { + let table = UniqueIndexWorkTable::default(); + + c.bench_function("unique_index_insert", |b| { + b.iter_batched( + || (), + |_| { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: fastrand::i64(..), + another: fastrand::u64(..), + }; + table.insert(black_box(row)) + }, + BatchSize::SmallInput, + ) + }); +} + +fn select_by_pk(c: &mut Criterion) { + let table = UniqueIndexWorkTable::default(); + let pks: Vec<_> = (0..1000) + .map(|_| { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: fastrand::i64(..), + another: fastrand::u64(..), + }; + table.insert(row).unwrap() + }) + .collect(); + + c.bench_function("unique_index_select_by_pk", |b| { + b.iter(|| { + let pk = pks[fastrand::usize(0..pks.len())].clone(); + black_box(table.select(pk)) + }) + }); +} + +fn select_by_unique_index(c: &mut Criterion) { + let table = UniqueIndexWorkTable::default(); + + for i in 1..=1000i64 { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: i, + another: i as u64, + }; + table.insert(row).unwrap(); + } + + c.bench_function("unique_index_select_by_test", |b| { + b.iter(|| { + let test = fastrand::i64(1..=1000); + black_box(table.select_by_test(test)) + }) + }); +} + +fn update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(UniqueIndexWorkTable::default()); + + let pks: Vec<_> = rt.block_on(async { + let mut pks = Vec::new(); + for i in 0..100u64 { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: i as i64, + another: i, + }; + pks.push(table.insert(row).unwrap()); + } + pks + }); + + c.bench_function("unique_index_update", |b| { + b.to_async(&rt).iter(|| async { + let idx = fastrand::usize(0..pks.len()); + let pk = pks[idx].clone(); + let row = UniqueIndexRow { + id: pk.into(), + test: fastrand::i64(..), + another: fastrand::u64(..), + }; + black_box(table.update(row).await) + }) + }); +} + +fn delete(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(UniqueIndexWorkTable::default()); + + c.bench_function("unique_index_delete", |b| { + b.iter_batched( + || { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: fastrand::i64(..), + another: fastrand::u64(..), + }; + table.insert(row).unwrap() + }, + |pk: UniqueIndexPrimaryKey| { + rt.block_on(async { + table.delete(black_box(pk)).await.unwrap() + }) + }, + BatchSize::SmallInput, + ) + }); +} + +fn upsert_insert(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(UniqueIndexWorkTable::default()); + + c.bench_function("unique_index_upsert_insert", |b| { + b.to_async(&rt).iter(|| async { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: fastrand::i64(..), + another: fastrand::u64(..), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn upsert_update(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let table = Arc::new(UniqueIndexWorkTable::default()); + + rt.block_on(async { + for i in 0..50u64 { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: i as i64, + another: i, + }; + table.upsert(row).await.unwrap(); + } + }); + + c.bench_function("unique_index_upsert_update", |b| { + b.to_async(&rt).iter(|| async { + let id = fastrand::u64(0..50); + let row = UniqueIndexRow { + id, + test: fastrand::i64(..), + another: fastrand::u64(..), + }; + black_box(table.upsert(row).await) + }) + }); +} + +fn batch_insert(c: &mut Criterion) { + let mut group = c.benchmark_group("unique_index_batch_insert"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter_batched( + || UniqueIndexWorkTable::default(), + |table: UniqueIndexWorkTable| { + for i in 0..size { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: i as i64, + another: i as u64, + }; + table.insert(black_box(row)).unwrap(); + } + black_box(table) + }, + BatchSize::SmallInput, + ) + }); + } + + group.finish(); +} + +fn batch_select_pk(c: &mut Criterion) { + let mut group = c.benchmark_group("unique_index_batch_select_pk"); + + for size in [100usize, 1_000, 10_000] { + group.throughput(Throughput::Elements(size as u64)); + + let table = UniqueIndexWorkTable::default(); + let pks: Vec<_> = (0..size) + .map(|_| { + let row = UniqueIndexRow { + id: table.get_next_pk().into(), + test: fastrand::i64(..), + another: fastrand::u64(..), + }; + table.insert(row).unwrap() + }) + .collect(); + + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter(|| { + for pk in &pks { + black_box(table.select(pk.clone())); + } + }) + }); + } + + group.finish(); +} + +criterion_group! { + name = unique_index_benchmarks; + config = crate::common::config::configure_criterion(); + targets = + insert, + select_by_pk, + select_by_unique_index, + update, + delete, + upsert_insert, + upsert_update, + batch_insert, + batch_select_pk, +} \ No newline at end of file diff --git a/benches/common/config.rs b/benches/common/config.rs new file mode 100644 index 0000000..9123ba8 --- /dev/null +++ b/benches/common/config.rs @@ -0,0 +1,9 @@ +use criterion::Criterion; +use std::time::Duration; + +pub fn configure_criterion() -> Criterion { + Criterion::default() + .sample_size(300) + .measurement_time(Duration::from_secs(10)) + .warm_up_time(Duration::from_secs(5)) +} \ No newline at end of file diff --git a/benches/common/mod.rs b/benches/common/mod.rs new file mode 100644 index 0000000..d2ea265 --- /dev/null +++ b/benches/common/mod.rs @@ -0,0 +1,69 @@ +pub mod config; + +use worktable::prelude::*; +use worktable::worktable; + +// Simple table for basic insert/select benchmarks +worktable!( + name: Simple, + columns: { + id: u64 primary_key autoincrement, + value: u64, + } +); + +// Table with unique index for index lookup benchmarks +worktable!( + name: UniqueIndex, + columns: { + id: u64 primary_key autoincrement, + test: i64, + another: u64, + }, + indexes: { + test_idx: test unique, + } +); + +// Table with non-unique index for multi-value lookups +worktable!( + name: NonUniqueIndex, + columns: { + id: u64 primary_key autoincrement, + value: u64, + category: u64, + }, + indexes: { + category_idx: category, + } +); + +// Table with String field and queries for async benchmarks +worktable!( + name: FullFeatured, + columns: { + id: u64 primary_key autoincrement, + val: i64, + val1: u64, + another: String, + something: u64, + }, + indexes: { + val1_idx: val1 unique, + another_idx: another, + }, + queries: { + in_place: { + ValById(val) by id, + } + update: { + AnotherById(another) by id, + SomethingById(something) by id, + AnotherByVal1(another) by val1, + }, + delete: { + ById() by id, + ByAnother() by another, + } + } +); \ No newline at end of file diff --git a/benches/worktable_benchmarks.rs b/benches/worktable_benchmarks.rs new file mode 100644 index 0000000..7d27e43 --- /dev/null +++ b/benches/worktable_benchmarks.rs @@ -0,0 +1,11 @@ +mod common; +mod cases; + +use criterion::criterion_main; + +criterion_main!( + cases::simple::simple_benchmarks, + cases::unique_index::unique_index_benchmarks, + cases::non_unique_index::non_unique_index_benchmarks, + cases::full_featured::full_featured_benchmarks, +); \ No newline at end of file From 1344ff6701492e9560fc5f0370ecfdb3369b3c71 Mon Sep 17 00:00:00 2001 From: Handy-caT <37216852+Handy-caT@users.noreply.github.com> Date: Wed, 6 May 2026 11:17:34 +0300 Subject: [PATCH 2/3] add contention bench for update and also do some corrections --- benches/cases/full_featured.rs | 4 ++-- benches/cases/mod.rs | 3 ++- benches/cases/non_unique_index.rs | 4 ++-- benches/cases/simple.rs | 4 ++-- benches/cases/unique_index.rs | 4 ++-- benches/worktable_benchmarks.rs | 1 + .../src/worktable/generator/primary_key.rs | 23 +++++++++++++++++++ 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/benches/cases/full_featured.rs b/benches/cases/full_featured.rs index ac990f3..a7814a6 100644 --- a/benches/cases/full_featured.rs +++ b/benches/cases/full_featured.rs @@ -43,7 +43,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("full_featured_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())].clone(); + let pk = pks[fastrand::usize(0..pks.len())]; black_box(table.select(pk)) }) }); @@ -365,7 +365,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(pk.clone())); + black_box(table.select(*pk)); } }) }); diff --git a/benches/cases/mod.rs b/benches/cases/mod.rs index eb40711..580567f 100644 --- a/benches/cases/mod.rs +++ b/benches/cases/mod.rs @@ -1,4 +1,5 @@ pub mod simple; pub mod unique_index; pub mod non_unique_index; -pub mod full_featured; \ No newline at end of file +pub mod full_featured; +pub mod update_contention; \ No newline at end of file diff --git a/benches/cases/non_unique_index.rs b/benches/cases/non_unique_index.rs index 25586b4..d34ae13 100644 --- a/benches/cases/non_unique_index.rs +++ b/benches/cases/non_unique_index.rs @@ -39,7 +39,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("non_unique_index_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())].clone(); + let pk = pks[fastrand::usize(0..pks.len())]; black_box(table.select(pk)) }) }); @@ -211,7 +211,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(pk.clone())); + black_box(table.select(*pk)); } }) }); diff --git a/benches/cases/simple.rs b/benches/cases/simple.rs index fef9693..6f7d771 100644 --- a/benches/cases/simple.rs +++ b/benches/cases/simple.rs @@ -36,7 +36,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("simple_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())].clone(); + let pk = pks[fastrand::usize(0..pks.len())]; black_box(table.select(pk)) }) }); @@ -180,7 +180,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(pk.clone())); + black_box(table.select(*pk)); } }) }); diff --git a/benches/cases/unique_index.rs b/benches/cases/unique_index.rs index 457b94d..6238841 100644 --- a/benches/cases/unique_index.rs +++ b/benches/cases/unique_index.rs @@ -38,7 +38,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("unique_index_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())].clone(); + let pk = pks[fastrand::usize(0..pks.len())]; black_box(table.select(pk)) }) }); @@ -212,7 +212,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(pk.clone())); + black_box(table.select(*pk)); } }) }); diff --git a/benches/worktable_benchmarks.rs b/benches/worktable_benchmarks.rs index 7d27e43..53e304c 100644 --- a/benches/worktable_benchmarks.rs +++ b/benches/worktable_benchmarks.rs @@ -8,4 +8,5 @@ criterion_main!( cases::unique_index::unique_index_benchmarks, cases::non_unique_index::non_unique_index_benchmarks, cases::full_featured::full_featured_benchmarks, + cases::update_contention::update_contention_benchmarks, ); \ No newline at end of file diff --git a/codegen/src/worktable/generator/primary_key.rs b/codegen/src/worktable/generator/primary_key.rs index 8398c8a..282ad1f 100644 --- a/codegen/src/worktable/generator/primary_key.rs +++ b/codegen/src/worktable/generator/primary_key.rs @@ -7,6 +7,17 @@ use crate::worktable::model::{GeneratorType, PrimaryKey}; use proc_macro2::{Ident, TokenStream}; use quote::quote; +const COPY_TYPES: &[&str] = &[ + "u8", "u16", "u32", "u64", "u128", "usize", + "i8", "i16", "i32", "i64", "i128", "isize", + "f32", "f64", + "bool", "char", +]; + +fn is_copy_type(type_str: &str) -> bool { + COPY_TYPES.contains(&type_str) || type_str == "uuid::Uuid" +} + impl Generator { /// Generates primary key type and it's impls. pub fn gen_primary_key_def(&mut self) -> syn::Result { @@ -56,6 +67,7 @@ impl Generator { .expect("should exist as got from definition") }) .collect::>(); + let unsized_derive = if is_unsized_vec(&types.iter().map(|v| v.to_string()).collect::>()) { quote! { @@ -65,9 +77,20 @@ impl Generator { quote! {} }; + let all_types_copy = types + .iter() + .all(|t| is_copy_type(&t.to_string().replace(" ", ""))); + + let copy_derive = if all_types_copy { + quote! { Copy, } + } else { + quote! {} + }; + quote! { #[derive( Clone, + #copy_derive rkyv::Archive, Debug, Default, From c408d19e1c93c0e0db1489952ef8ce4121f4d0e8 Mon Sep 17 00:00:00 2001 From: Handy-caT <37216852+Handy-caT@users.noreply.github.com> Date: Wed, 6 May 2026 11:39:49 +0300 Subject: [PATCH 3/3] fix benches --- benches/cases/full_featured.rs | 6 +++--- benches/cases/non_unique_index.rs | 6 +++--- benches/cases/simple.rs | 6 +++--- benches/cases/unique_index.rs | 6 +++--- tests/migration/mod.rs | 2 +- tests/worktable/index/order.rs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/benches/cases/full_featured.rs b/benches/cases/full_featured.rs index a7814a6..f8b9053 100644 --- a/benches/cases/full_featured.rs +++ b/benches/cases/full_featured.rs @@ -43,7 +43,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("full_featured_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())]; + let pk = pks[fastrand::usize(0..pks.len())].clone(); black_box(table.select(pk)) }) }); @@ -320,7 +320,7 @@ fn batch_insert(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter_batched( - || FullFeaturedWorkTable::default(), + FullFeaturedWorkTable::default, |table: FullFeaturedWorkTable| { for i in 0..size { let row = FullFeaturedRow { @@ -365,7 +365,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(*pk)); + black_box(table.select(pk.clone())); } }) }); diff --git a/benches/cases/non_unique_index.rs b/benches/cases/non_unique_index.rs index d34ae13..85cdaa8 100644 --- a/benches/cases/non_unique_index.rs +++ b/benches/cases/non_unique_index.rs @@ -39,7 +39,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("non_unique_index_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())]; + let pk = pks[fastrand::usize(0..pks.len())].clone(); black_box(table.select(pk)) }) }); @@ -170,7 +170,7 @@ fn batch_insert(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter_batched( - || NonUniqueIndexWorkTable::default(), + NonUniqueIndexWorkTable::default, |table: NonUniqueIndexWorkTable| { for i in 0..size { let row = NonUniqueIndexRow { @@ -211,7 +211,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(*pk)); + black_box(table.select(pk.clone())); } }) }); diff --git a/benches/cases/simple.rs b/benches/cases/simple.rs index 6f7d771..0c1a4ae 100644 --- a/benches/cases/simple.rs +++ b/benches/cases/simple.rs @@ -36,7 +36,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("simple_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())]; + let pk = pks[fastrand::usize(0..pks.len())].clone(); black_box(table.select(pk)) }) }); @@ -141,7 +141,7 @@ fn batch_insert(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter_batched( - || SimpleWorkTable::default(), + SimpleWorkTable::default, |table: SimpleWorkTable| { for i in 0..size { let row = SimpleRow { @@ -180,7 +180,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(*pk)); + black_box(table.select(pk.clone())); } }) }); diff --git a/benches/cases/unique_index.rs b/benches/cases/unique_index.rs index 6238841..6fcc6e0 100644 --- a/benches/cases/unique_index.rs +++ b/benches/cases/unique_index.rs @@ -38,7 +38,7 @@ fn select_by_pk(c: &mut Criterion) { c.bench_function("unique_index_select_by_pk", |b| { b.iter(|| { - let pk = pks[fastrand::usize(0..pks.len())]; + let pk = pks[fastrand::usize(0..pks.len())].clone(); black_box(table.select(pk)) }) }); @@ -171,7 +171,7 @@ fn batch_insert(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter_batched( - || UniqueIndexWorkTable::default(), + UniqueIndexWorkTable::default, |table: UniqueIndexWorkTable| { for i in 0..size { let row = UniqueIndexRow { @@ -212,7 +212,7 @@ fn batch_select_pk(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { for pk in &pks { - black_box(table.select(*pk)); + black_box(table.select(pk.clone())); } }) }); diff --git a/tests/migration/mod.rs b/tests/migration/mod.rs index dadfd01..1d24066 100644 --- a/tests/migration/mod.rs +++ b/tests/migration/mod.rs @@ -72,7 +72,7 @@ impl Migration for UserMigration { fn migrate(row: v2::UserRow, ctx: &Self::Context) -> UserRow { UserRow { - id: row.id.into(), + id: row.id, name: row.name, email: row.email, created_at: ctx.default_created_at, diff --git a/tests/worktable/index/order.rs b/tests/worktable/index/order.rs index 77d41ca..98c6367 100644 --- a/tests/worktable/index/order.rs +++ b/tests/worktable/index/order.rs @@ -29,7 +29,7 @@ fn index_struct_field_order() { #[test] fn available_indexes_enum_order() { - let variants = vec![ + let variants = [ OrderTestAvailableIndexes::IdxC, OrderTestAvailableIndexes::IdxA, OrderTestAvailableIndexes::IdxB,