diff --git a/Cargo.toml b/Cargo.toml index 251a18c..aece7c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,4 +50,9 @@ worktable_codegen = { path = "codegen", version = "=0.9.0-beta0.2.0" } [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..f8b9053 --- /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..580567f --- /dev/null +++ b/benches/cases/mod.rs @@ -0,0 +1,5 @@ +pub mod simple; +pub mod unique_index; +pub mod non_unique_index; +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 new file mode 100644 index 0000000..85cdaa8 --- /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..0c1a4ae --- /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..6fcc6e0 --- /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..53e304c --- /dev/null +++ b/benches/worktable_benchmarks.rs @@ -0,0 +1,12 @@ +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, + cases::update_contention::update_contention_benchmarks, +); \ No newline at end of file diff --git a/codegen/src/generators/in_memory/primary_key.rs b/codegen/src/generators/in_memory/primary_key.rs index cc98276..9dd0036 100644 --- a/codegen/src/generators/in_memory/primary_key.rs +++ b/codegen/src/generators/in_memory/primary_key.rs @@ -56,6 +56,7 @@ impl InMemoryGenerator { .expect("should exist as got from definition") }) .collect::>(); + let unsized_derive = if is_unsized_vec(&types.iter().map(|v| v.to_string()).collect::>()) { quote! { 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,