diff --git a/.gitignore b/.gitignore index dfcfd56..90f7c20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,25 @@ +# Generated by Cargo +# will have compiled files and executables +debug +target + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## diff --git a/README.md b/README.md index 9cda4aa..38f4475 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Both databases used to store and retrieve doublet-links representation. To suppo ## Results The results below represent the amount of time (ns) the operation takes per iteration. - First picture shows time in a pixel scale (for doublets just minimum value is shown, otherwise it will be not present on the graph). -- Second picture shows time in a logarithmic scale (to see diffrence clearly, because it is around 2-3 orders of magnitude). +- Second picture shows time in a logarithmic scale (to see difference clearly, because it is around 2-3 orders of magnitude). ### Rust ![Image of Rust benchmark (pixel scale)](https://github.com/linksplatform/Comparisons.PostgreSQLVSDoublets/blob/main/Docs/bench_rust.png?raw=true) diff --git a/rust/benches/bench.rs b/rust/benches/bench.rs index 5668948..9240b6a 100644 --- a/rust/benches/bench.rs +++ b/rust/benches/bench.rs @@ -2,8 +2,11 @@ use { benchmarks::{ - create_links, delete_links, each_all, each_concrete, each_identity, each_incoming, - each_outgoing, update_links, + doublets_create_links, doublets_delete_links, doublets_each_all, doublets_each_concrete, + doublets_each_identity, doublets_each_incoming, doublets_each_outgoing, + doublets_update_links, psql_create_links, psql_delete_links, psql_each_all, + psql_each_concrete, psql_each_identity, psql_each_incoming, psql_each_outgoing, + psql_update_links, }, criterion::{criterion_group, criterion_main}, }; @@ -20,15 +23,30 @@ macro_rules! tri { pub(crate) use tri; +// PostgreSQL benchmarks criterion_group!( - benches, - create_links, - delete_links, - each_identity, - each_concrete, - each_outgoing, - each_incoming, - each_all, - update_links + psql_benches, + psql_create_links, + psql_delete_links, + psql_each_identity, + psql_each_concrete, + psql_each_outgoing, + psql_each_incoming, + psql_each_all, + psql_update_links ); -criterion_main!(benches); + +// Doublets benchmarks +criterion_group!( + doublets_benches, + doublets_create_links, + doublets_delete_links, + doublets_each_identity, + doublets_each_concrete, + doublets_each_outgoing, + doublets_each_incoming, + doublets_each_all, + doublets_update_links +); + +criterion_main!(psql_benches, doublets_benches); diff --git a/rust/benches/benchmarks/create.rs b/rust/benches/benchmarks/doublets/create.rs similarity index 59% rename from rust/benches/benchmarks/create.rs rename to rust/benches/benchmarks/doublets/create.rs index 8b51cbb..49296d9 100644 --- a/rust/benches/benchmarks/create.rs +++ b/rust/benches/benchmarks/doublets/create.rs @@ -1,18 +1,33 @@ -use { - crate::tri, - criterion::{BenchmarkGroup, Criterion, measurement::WallTime}, - doublets::{ - data::LinkType, - Doublets, - mem::{Alloc, FileMapped}, - parts::LinkPart, - split::{self, DataPart, IndexPart}, unit, - }, - linkspsql::{bench, benchmark_links, Benched, Client, connect, Exclusive, Fork, Transaction}, - std::{alloc::Global, time::{Duration, Instant}}, +//! # Doublets Create Links Benchmark +//! +//! This benchmark measures the performance of creating new links in Doublets. +//! +//! ## Implementation +//! +//! Doublets creates links by: +//! - Allocating next available ID from internal counter +//! - Writing (id, id, id) tuple directly to memory/file +//! - Updating source and target indexes +//! - Time complexity: O(log n) for index updates + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + mem::{Alloc, FileMapped}, + parts::LinkPart, + split::{self, DataPart, IndexPart}, + unit, Doublets, }; +use linkspsql::{bench, benchmark_links, Benched, Fork}; -fn bench>( +use crate::tri; + +/// Runs the create benchmark on a Doublets backend. +fn bench>( group: &mut BenchmarkGroup, id: &str, mut benched: B, @@ -27,19 +42,10 @@ fn bench>( }); } +/// Creates benchmark for Doublets backends on link creation. pub fn create_links(c: &mut Criterion) { let mut group = c.benchmark_group("Create"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -68,5 +74,6 @@ pub fn create_links(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/delete.rs b/rust/benches/benchmarks/doublets/delete.rs similarity index 65% rename from rust/benches/benchmarks/delete.rs rename to rust/benches/benchmarks/doublets/delete.rs index 01c9425..8ae270d 100644 --- a/rust/benches/benchmarks/delete.rs +++ b/rust/benches/benchmarks/doublets/delete.rs @@ -1,15 +1,31 @@ -use { - crate::tri, - criterion::{BenchmarkGroup, Criterion, measurement::WallTime}, - doublets::{ - Doublets, - mem::{Alloc, FileMapped}, - parts::LinkPart, - split::{self, DataPart, IndexPart}, unit, - }, - linkspsql::{bench, background_links, benchmark_links, Benched, Client, connect, Exclusive, Fork, Transaction}, - std::{alloc::Global, time::{Duration, Instant}}, +//! # Doublets Delete Links Benchmark +//! +//! This benchmark measures the performance of deleting links in Doublets. +//! +//! ## Implementation +//! +//! Doublets deletes links by: +//! - Removing the link from source and target indexes +//! - Marking the slot as free for reuse +//! - Time complexity: O(log n) for index updates + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + mem::{Alloc, FileMapped}, + parts::LinkPart, + split::{self, DataPart, IndexPart}, + unit, Doublets, }; +use linkspsql::{background_links, bench, benchmark_links, Benched, Fork}; + +use crate::tri; + +/// Runs the delete benchmark on a Doublets backend. fn bench>( group: &mut BenchmarkGroup, id: &str, @@ -29,19 +45,10 @@ fn bench>( }); } +/// Creates benchmark for Doublets backends on link deletion. pub fn delete_links(c: &mut Criterion) { let mut group = c.benchmark_group("Delete"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -70,5 +77,6 @@ pub fn delete_links(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/each/all.rs b/rust/benches/benchmarks/doublets/each/all.rs similarity index 60% rename from rust/benches/benchmarks/each/all.rs rename to rust/benches/benchmarks/doublets/each/all.rs index 00d46c0..1dbc9e9 100644 --- a/rust/benches/benchmarks/each/all.rs +++ b/rust/benches/benchmarks/doublets/each/all.rs @@ -1,21 +1,32 @@ -use { - crate::tri, - criterion::{measurement::WallTime, BenchmarkGroup, Criterion}, - doublets::{ - data::{Flow, LinkType}, - mem::{Alloc, FileMapped}, - parts::LinkPart, - split::{self, DataPart, IndexPart}, - unit, Doublets, - }, - linkspsql::{bench, connect, Benched, Client, Exclusive, Fork, Transaction}, - std::{ - alloc::Global, - time::{Duration, Instant}, - }, +//! # Doublets Each All Benchmark +//! +//! This benchmark measures the performance of querying all links in Doublets. +//! +//! ## Implementation +//! +//! Query pattern: `[*, *, *]` - matches all links +//! - Sequential iteration through the internal array +//! - Time complexity: O(n) where n is the number of links + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::Flow, + mem::{Alloc, FileMapped}, + parts::LinkPart, + split::{self, DataPart, IndexPart}, + unit, Doublets, }; +use linkspsql::{bench, Benched, Fork}; -fn bench>( +use crate::tri; + +/// Runs the each_all benchmark on a Doublets backend. +fn bench>( group: &mut BenchmarkGroup, id: &str, mut benched: B, @@ -28,19 +39,10 @@ fn bench>( }); } +/// Creates benchmark for Doublets backends on querying all links. pub fn each_all(c: &mut Criterion) { let mut group = c.benchmark_group("Each_All"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -69,5 +71,6 @@ pub fn each_all(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/each/concrete.rs b/rust/benches/benchmarks/doublets/each/concrete.rs similarity index 62% rename from rust/benches/benchmarks/each/concrete.rs rename to rust/benches/benchmarks/doublets/each/concrete.rs index a10457c..42aa6a1 100644 --- a/rust/benches/benchmarks/each/concrete.rs +++ b/rust/benches/benchmarks/doublets/each/concrete.rs @@ -1,19 +1,31 @@ -use { - crate::tri, - criterion::{measurement::WallTime, BenchmarkGroup, Criterion}, - doublets::{ - data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, - split::{self, DataPart, IndexPart}, - unit, Doublets, - }, - linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}, - std::{ - alloc::Global, - time::{Duration, Instant}, - }, +//! # Doublets Each Concrete Benchmark +//! +//! This benchmark measures the performance of querying links by source+target in Doublets. +//! +//! ## Implementation +//! +//! Query pattern: `[*, src, tgt]` - matches links by source and target +//! - Index tree lookup followed by filter +//! - Time complexity: O(log n) for index lookup + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + mem::{Alloc, FileMapped}, + parts::LinkPart, + split::{self, DataPart, IndexPart}, + unit, Doublets, }; +use linkspsql::{background_links, bench, Benched, Fork}; + +use crate::tri; + +/// Runs the each_concrete benchmark on a Doublets backend. fn bench>( group: &mut BenchmarkGroup, id: &str, @@ -31,19 +43,10 @@ fn bench>( }); } +/// Creates benchmark for Doublets backends on querying links by source+target. pub fn each_concrete(c: &mut Criterion) { let mut group = c.benchmark_group("Each_Concrete"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -72,5 +75,6 @@ pub fn each_concrete(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/each/identity.rs b/rust/benches/benchmarks/doublets/each/identity.rs similarity index 64% rename from rust/benches/benchmarks/each/identity.rs rename to rust/benches/benchmarks/doublets/each/identity.rs index 40386de..a68eee7 100644 --- a/rust/benches/benchmarks/each/identity.rs +++ b/rust/benches/benchmarks/doublets/each/identity.rs @@ -1,20 +1,31 @@ -use { - crate::tri, - criterion::{measurement::WallTime, BenchmarkGroup, Criterion}, - doublets::{ - data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, - split::{self, DataPart, IndexPart}, - unit, Doublets, - }, - linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}, - std::{ - alloc::Global, - time::{Duration, Instant}, - }, +//! # Doublets Each Identity Benchmark +//! +//! This benchmark measures the performance of querying links by ID in Doublets. +//! +//! ## Implementation +//! +//! Query pattern: `[id, *, *]` - matches link by ID +//! - Direct array access: `links[id]` +//! - Time complexity: O(1) + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + mem::{Alloc, FileMapped}, + parts::LinkPart, + split::{self, DataPart, IndexPart}, + unit, Doublets, }; +use linkspsql::{background_links, bench, Benched, Fork}; +use crate::tri; + +/// Runs the each_identity benchmark on a Doublets backend. fn bench>( group: &mut BenchmarkGroup, id: &str, @@ -32,19 +43,10 @@ fn bench>( }); } +/// Creates benchmark for Doublets backends on querying links by ID. pub fn each_identity(c: &mut Criterion) { let mut group = c.benchmark_group("Each_Identity"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -73,5 +75,6 @@ pub fn each_identity(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/each/incoming.rs b/rust/benches/benchmarks/doublets/each/incoming.rs similarity index 63% rename from rust/benches/benchmarks/each/incoming.rs rename to rust/benches/benchmarks/doublets/each/incoming.rs index 3b13bda..81ade62 100644 --- a/rust/benches/benchmarks/each/incoming.rs +++ b/rust/benches/benchmarks/doublets/each/incoming.rs @@ -1,20 +1,31 @@ -use { - crate::tri, - criterion::{measurement::WallTime, BenchmarkGroup, Criterion}, - doublets::{ - data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, - split::{self, DataPart, IndexPart}, - unit, Doublets, - }, - linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}, - std::{ - alloc::Global, - time::{Duration, Instant}, - }, +//! # Doublets Each Incoming Benchmark +//! +//! This benchmark measures the performance of querying links by target in Doublets. +//! +//! ## Implementation +//! +//! Query pattern: `[*, *, tgt]` - matches links by target +//! - Target index tree traversal +//! - Time complexity: O(log n + k) where k is the number of matching links + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + mem::{Alloc, FileMapped}, + parts::LinkPart, + split::{self, DataPart, IndexPart}, + unit, Doublets, }; +use linkspsql::{background_links, bench, Benched, Fork}; +use crate::tri; + +/// Runs the each_incoming benchmark on a Doublets backend. fn bench>( group: &mut BenchmarkGroup, id: &str, @@ -32,19 +43,10 @@ fn bench>( }); } +/// Creates benchmark for Doublets backends on querying links by target. pub fn each_incoming(c: &mut Criterion) { let mut group = c.benchmark_group("Each_Incoming"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -73,5 +75,6 @@ pub fn each_incoming(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/doublets/each/mod.rs b/rust/benches/benchmarks/doublets/each/mod.rs new file mode 100644 index 0000000..ea8254e --- /dev/null +++ b/rust/benches/benchmarks/doublets/each/mod.rs @@ -0,0 +1,25 @@ +//! # Doublets Query Benchmarks +//! +//! This module contains query (each) benchmarks for Doublets backends. +//! +//! ## Query Types +//! +//! | Benchmark | Query Pattern | Implementation | +//! |-----------------|--------------------|-------------------------------| +//! | `each_all` | `[*, *, *]` | Sequential array iteration | +//! | `each_identity` | `[id, *, *]` | Direct array access | +//! | `each_concrete` | `[*, src, tgt]` | Index tree lookup + filter | +//! | `each_outgoing` | `[*, src, *]` | Source index tree traversal | +//! | `each_incoming` | `[*, *, tgt]` | Target index tree traversal | + +mod all; +mod concrete; +mod identity; +mod incoming; +mod outgoing; + +pub use all::each_all; +pub use concrete::each_concrete; +pub use identity::each_identity; +pub use incoming::each_incoming; +pub use outgoing::each_outgoing; diff --git a/rust/benches/benchmarks/each/outgoing.rs b/rust/benches/benchmarks/doublets/each/outgoing.rs similarity index 63% rename from rust/benches/benchmarks/each/outgoing.rs rename to rust/benches/benchmarks/doublets/each/outgoing.rs index cb27384..93cbc9b 100644 --- a/rust/benches/benchmarks/each/outgoing.rs +++ b/rust/benches/benchmarks/doublets/each/outgoing.rs @@ -1,20 +1,31 @@ -use { - crate::tri, - criterion::{measurement::WallTime, BenchmarkGroup, Criterion}, - doublets::{ - data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, - split::{self, DataPart, IndexPart}, - unit, Doublets, - }, - linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}, - std::{ - alloc::Global, - time::{Duration, Instant}, - }, +//! # Doublets Each Outgoing Benchmark +//! +//! This benchmark measures the performance of querying links by source in Doublets. +//! +//! ## Implementation +//! +//! Query pattern: `[*, src, *]` - matches links by source +//! - Source index tree traversal +//! - Time complexity: O(log n + k) where k is the number of matching links + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + mem::{Alloc, FileMapped}, + parts::LinkPart, + split::{self, DataPart, IndexPart}, + unit, Doublets, }; +use linkspsql::{background_links, bench, Benched, Fork}; +use crate::tri; + +/// Runs the each_outgoing benchmark on a Doublets backend. fn bench>( group: &mut BenchmarkGroup, id: &str, @@ -32,19 +43,10 @@ fn bench>( }); } +/// Creates benchmark for Doublets backends on querying links by source. pub fn each_outgoing(c: &mut Criterion) { let mut group = c.benchmark_group("Each_Outgoing"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -73,5 +75,6 @@ pub fn each_outgoing(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/doublets/mod.rs b/rust/benches/benchmarks/doublets/mod.rs new file mode 100644 index 0000000..0222115 --- /dev/null +++ b/rust/benches/benchmarks/doublets/mod.rs @@ -0,0 +1,35 @@ +//! # Doublets Benchmark Implementations +//! +//! This module contains all benchmark implementations for Doublets. +//! Each benchmark tests a specific database operation using direct memory access +//! to specialized data structures. +//! +//! ## Benchmarked Operations +//! +//! | Benchmark | Implementation | +//! |-----------------|-------------------------------------------------| +//! | `create_links` | Allocate ID, write to memory, update indexes | +//! | `delete_links` | Remove from indexes, mark slot as free | +//! | `update_links` | Update storage, re-index if values changed | +//! | `each_all` | Sequential array iteration | +//! | `each_identity` | Direct array access: `links[id]` | +//! | `each_concrete` | Index tree lookup + filter | +//! | `each_outgoing` | Source index tree traversal | +//! | `each_incoming` | Target index tree traversal | +//! +//! ## Storage Backends Tested +//! +//! - `Doublets_United_Volatile` - In-memory unit storage +//! - `Doublets_United_NonVolatile` - File-mapped unit storage +//! - `Doublets_Split_Volatile` - In-memory split storage (separate data/index) +//! - `Doublets_Split_NonVolatile` - File-mapped split storage + +mod create; +mod delete; +pub mod each; +mod update; + +pub use create::create_links; +pub use delete::delete_links; +pub use each::*; +pub use update::update_links; diff --git a/rust/benches/benchmarks/update.rs b/rust/benches/benchmarks/doublets/update.rs similarity index 64% rename from rust/benches/benchmarks/update.rs rename to rust/benches/benchmarks/doublets/update.rs index 75cb273..36b5b5f 100644 --- a/rust/benches/benchmarks/update.rs +++ b/rust/benches/benchmarks/doublets/update.rs @@ -1,15 +1,31 @@ -use { - crate::tri, - criterion::{BenchmarkGroup, Criterion, measurement::WallTime}, - doublets::{ - Doublets, - mem::{Alloc, FileMapped}, split, split::{DataPart, IndexPart}, - unit, unit::LinkPart, - }, - linkspsql::{background_links, benchmark_links, bench, Benched, Transaction, Exclusive, connect, Client, Fork}, - std::{alloc::Global, time::{Duration, Instant}}, +//! # Doublets Update Links Benchmark +//! +//! This benchmark measures the performance of updating links in Doublets. +//! +//! ## Implementation +//! +//! Doublets updates links by: +//! - Modifying source and target values in storage +//! - Re-indexing if source or target changed +//! - Time complexity: O(log n) for index updates + +use std::{ + alloc::Global, + time::{Duration, Instant}, +}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + mem::{Alloc, FileMapped}, + split::{self, DataPart, IndexPart}, + unit::{self, LinkPart}, + Doublets, }; +use linkspsql::{background_links, bench, benchmark_links, Benched, Fork}; + +use crate::tri; +/// Runs the update benchmark on a Doublets backend. fn bench>( group: &mut BenchmarkGroup, id: &str, @@ -26,19 +42,11 @@ fn bench>( })(bencher, &mut benched); }); } + +/// Creates benchmark for Doublets backends on link updates. pub fn update_links(c: &mut Criterion) { let mut group = c.benchmark_group("Update"); - tri! { - bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); - } - tri! { - let mut client = connect().unwrap(); - bench( - &mut group, - "PSQL_Transaction", - Exclusive::>::setup(&mut client).unwrap(), - ); - } + tri! { bench( &mut group, @@ -67,5 +75,6 @@ pub fn update_links(c: &mut Criterion) { split::Store::, FileMapped<_>>::setup(("split_index.links", "split_data.links")).unwrap() ) } + group.finish(); } diff --git a/rust/benches/benchmarks/each/mod.rs b/rust/benches/benchmarks/each/mod.rs deleted file mode 100644 index d561cdf..0000000 --- a/rust/benches/benchmarks/each/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod all; -mod concrete; -mod identity; -mod incoming; -mod outgoing; - -pub use { - all::each_all, concrete::each_concrete, identity::each_identity, incoming::each_incoming, - outgoing::each_outgoing, -}; diff --git a/rust/benches/benchmarks/mod.rs b/rust/benches/benchmarks/mod.rs index 2d14b2a..eb2bc12 100644 --- a/rust/benches/benchmarks/mod.rs +++ b/rust/benches/benchmarks/mod.rs @@ -1,5 +1,59 @@ -mod create; -mod delete; -mod each; -mod update; -pub use {create::create_links, delete::delete_links, each::*, update::update_links}; +//! # Benchmark Implementations +//! +//! This module contains all benchmark implementations comparing PostgreSQL and Doublets. +//! Each benchmark tests a specific database operation across all storage backends. +//! +//! ## Module Structure +//! +//! The benchmarks are split into two separate modules for clear comparison: +//! +//! - **[`psql`]** - All PostgreSQL benchmarks using SQL queries via libpq +//! - **[`doublets`]** - All Doublets benchmarks using direct memory access +//! +//! ## Benchmarked Operations +//! +//! | Benchmark | Operation | What it measures | +//! |-----------------|------------------------------------------------|-------------------------------------| +//! | `create_links` | Insert point links (id = source = target) | Write performance | +//! | `delete_links` | Remove links by ID | Delete performance | +//! | `update_links` | Modify source/target of existing links | Update performance | +//! | `each_all` | Query all links `[*, *, *]` | Full scan performance | +//! | `each_identity` | Query by ID `[id, *, *]` | Primary key lookup | +//! | `each_concrete` | Query by source+target `[*, src, tgt]` | Composite index lookup | +//! | `each_outgoing` | Query by source `[*, src, *]` | Source index lookup | +//! | `each_incoming` | Query by target `[*, *, tgt]` | Target index lookup | +//! +//! ## Storage Backends Tested +//! +//! ### Doublets (4 variants) +//! - `Doublets_United_Volatile` - In-memory unit storage +//! - `Doublets_United_NonVolatile` - File-mapped unit storage +//! - `Doublets_Split_Volatile` - In-memory split storage (separate data/index) +//! - `Doublets_Split_NonVolatile` - File-mapped split storage +//! +//! ### PostgreSQL (2 variants) +//! - `PSQL_NonTransaction` - Direct SQL execution +//! - `PSQL_Transaction` - SQL execution within explicit transaction + +pub mod doublets; +pub mod psql; + +// Re-export all PostgreSQL benchmarks with psql_ prefix +pub use psql::create_links as psql_create_links; +pub use psql::delete_links as psql_delete_links; +pub use psql::each_all as psql_each_all; +pub use psql::each_concrete as psql_each_concrete; +pub use psql::each_identity as psql_each_identity; +pub use psql::each_incoming as psql_each_incoming; +pub use psql::each_outgoing as psql_each_outgoing; +pub use psql::update_links as psql_update_links; + +// Re-export all Doublets benchmarks with doublets_ prefix +pub use self::doublets::create_links as doublets_create_links; +pub use self::doublets::delete_links as doublets_delete_links; +pub use self::doublets::each_all as doublets_each_all; +pub use self::doublets::each_concrete as doublets_each_concrete; +pub use self::doublets::each_identity as doublets_each_identity; +pub use self::doublets::each_incoming as doublets_each_incoming; +pub use self::doublets::each_outgoing as doublets_each_outgoing; +pub use self::doublets::update_links as doublets_update_links; diff --git a/rust/benches/benchmarks/psql/create.rs b/rust/benches/benchmarks/psql/create.rs new file mode 100644 index 0000000..061162a --- /dev/null +++ b/rust/benches/benchmarks/psql/create.rs @@ -0,0 +1,57 @@ +//! # PostgreSQL Create Links Benchmark +//! +//! This benchmark measures the performance of creating new links in PostgreSQL. +//! +//! ## Implementation +//! +//! PostgreSQL executes this SQL query: +//! ```sql +//! INSERT INTO links (id, source, target) VALUES ($1, $1, $1) +//! ``` +//! +//! - Makes connection to PostgreSQL database +//! - Executes INSERT statement +//! - Time complexity: O(log n) for B-tree index updates + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::Doublets; +use linkspsql::{bench, benchmark_links, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the create benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let links = benchmark_links(); + group.bench_function(id, |bencher| { + bench!(|fork| as B { + for _ in 0..links { + let _ = elapsed! {fork.create_point()?}; + } + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on link creation. +pub fn create_links(c: &mut Criterion) { + let mut group = c.benchmark_group("Create"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/benches/benchmarks/psql/delete.rs b/rust/benches/benchmarks/psql/delete.rs new file mode 100644 index 0000000..b3a5174 --- /dev/null +++ b/rust/benches/benchmarks/psql/delete.rs @@ -0,0 +1,61 @@ +//! # PostgreSQL Delete Links Benchmark +//! +//! This benchmark measures the performance of deleting links in PostgreSQL. +//! +//! ## Implementation +//! +//! PostgreSQL executes this SQL query: +//! ```sql +//! DELETE FROM links WHERE id = $1 +//! ``` +//! +//! - Makes connection to PostgreSQL database +//! - Executes DELETE statement +//! - Time complexity: O(log n) for B-tree index updates + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::Doublets; +use linkspsql::{background_links, bench, benchmark_links, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the delete benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let bg_links = background_links(); + let links = benchmark_links(); + group.bench_function(id, |bencher| { + bench!(|fork| as B { + for _prepare in bg_links..bg_links + links { + let _ = fork.create_point(); + } + for id in (bg_links..=bg_links + links).rev() { + let _ = elapsed! {fork.delete(id)?}; + } + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on link deletion. +pub fn delete_links(c: &mut Criterion) { + let mut group = c.benchmark_group("Delete"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/benches/benchmarks/psql/each/all.rs b/rust/benches/benchmarks/psql/each/all.rs new file mode 100644 index 0000000..84a4a34 --- /dev/null +++ b/rust/benches/benchmarks/psql/each/all.rs @@ -0,0 +1,54 @@ +//! # PostgreSQL Each All Benchmark +//! +//! This benchmark measures the performance of querying all links in PostgreSQL. +//! +//! ## Implementation +//! +//! SQL query: +//! ```sql +//! SELECT id, source, target FROM links +//! ``` +//! +//! - Full table scan +//! - Time complexity: O(n) where n is the number of links + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{data::Flow, Doublets}; +use linkspsql::{bench, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the each_all benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let handler = |_| Flow::Continue; + group.bench_function(id, |bencher| { + bench!(|fork| as B { + let _ = elapsed! { fork.each(handler) }; + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on querying all links. +pub fn each_all(c: &mut Criterion) { + let mut group = c.benchmark_group("Each_All"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/benches/benchmarks/psql/each/concrete.rs b/rust/benches/benchmarks/psql/each/concrete.rs new file mode 100644 index 0000000..43b4614 --- /dev/null +++ b/rust/benches/benchmarks/psql/each/concrete.rs @@ -0,0 +1,61 @@ +//! # PostgreSQL Each Concrete Benchmark +//! +//! This benchmark measures the performance of querying links by source+target in PostgreSQL. +//! +//! ## Implementation +//! +//! SQL query: +//! ```sql +//! SELECT id, source, target FROM links WHERE source = $1 AND target = $2 +//! ``` +//! +//! - Composite index lookup +//! - Time complexity: O(log n) + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + Doublets, +}; +use linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the each_concrete benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let handler = |_| Flow::Continue; + let any = LinksConstants::new().any; + let bg_links = background_links(); + group.bench_function(id, |bencher| { + bench!(|fork| as B { + for index in 1..=bg_links { + elapsed! {fork.each_by([any, index, index], handler)}; + } + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on querying links by source+target. +pub fn each_concrete(c: &mut Criterion) { + let mut group = c.benchmark_group("Each_Concrete"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/benches/benchmarks/psql/each/identity.rs b/rust/benches/benchmarks/psql/each/identity.rs new file mode 100644 index 0000000..c376df0 --- /dev/null +++ b/rust/benches/benchmarks/psql/each/identity.rs @@ -0,0 +1,61 @@ +//! # PostgreSQL Each Identity Benchmark +//! +//! This benchmark measures the performance of querying links by ID in PostgreSQL. +//! +//! ## Implementation +//! +//! SQL query: +//! ```sql +//! SELECT id, source, target FROM links WHERE id = $1 +//! ``` +//! +//! - Primary key lookup using B-tree index +//! - Time complexity: O(log n) + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + Doublets, +}; +use linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the each_identity benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let handler = |_| Flow::Continue; + let any = LinksConstants::new().any; + let bg_links = background_links(); + group.bench_function(id, |bencher| { + bench!(|fork| as B { + for index in 1..=bg_links { + elapsed! {fork.each_by([index, any, any], handler)}; + } + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on querying links by ID. +pub fn each_identity(c: &mut Criterion) { + let mut group = c.benchmark_group("Each_Identity"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/benches/benchmarks/psql/each/incoming.rs b/rust/benches/benchmarks/psql/each/incoming.rs new file mode 100644 index 0000000..02f1b55 --- /dev/null +++ b/rust/benches/benchmarks/psql/each/incoming.rs @@ -0,0 +1,61 @@ +//! # PostgreSQL Each Incoming Benchmark +//! +//! This benchmark measures the performance of querying links by target in PostgreSQL. +//! +//! ## Implementation +//! +//! SQL query: +//! ```sql +//! SELECT id, source, target FROM links WHERE target = $1 +//! ``` +//! +//! - Target column index lookup +//! - Time complexity: O(log n + k) where k is number of matching links + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + Doublets, +}; +use linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the each_incoming benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let handler = |_| Flow::Continue; + let any = LinksConstants::new().any; + let bg_links = background_links(); + group.bench_function(id, |bencher| { + bench!(|fork| as B { + for index in 1..=bg_links { + elapsed! {fork.each_by([any, any, index], handler)}; + } + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on querying links by target. +pub fn each_incoming(c: &mut Criterion) { + let mut group = c.benchmark_group("Each_Incoming"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/benches/benchmarks/psql/each/mod.rs b/rust/benches/benchmarks/psql/each/mod.rs new file mode 100644 index 0000000..ea37773 --- /dev/null +++ b/rust/benches/benchmarks/psql/each/mod.rs @@ -0,0 +1,25 @@ +//! # PostgreSQL Query Benchmarks +//! +//! This module contains query (each) benchmarks for PostgreSQL backends. +//! +//! ## Query Types +//! +//! | Benchmark | SQL Pattern | +//! |-----------------|-----------------------------------------------| +//! | `each_all` | `SELECT id, source, target FROM links` | +//! | `each_identity` | `SELECT ... WHERE id = $1` | +//! | `each_concrete` | `SELECT ... WHERE source = $1 AND target = $2`| +//! | `each_outgoing` | `SELECT ... WHERE source = $1` | +//! | `each_incoming` | `SELECT ... WHERE target = $1` | + +mod all; +mod concrete; +mod identity; +mod incoming; +mod outgoing; + +pub use all::each_all; +pub use concrete::each_concrete; +pub use identity::each_identity; +pub use incoming::each_incoming; +pub use outgoing::each_outgoing; diff --git a/rust/benches/benchmarks/psql/each/outgoing.rs b/rust/benches/benchmarks/psql/each/outgoing.rs new file mode 100644 index 0000000..60890a3 --- /dev/null +++ b/rust/benches/benchmarks/psql/each/outgoing.rs @@ -0,0 +1,61 @@ +//! # PostgreSQL Each Outgoing Benchmark +//! +//! This benchmark measures the performance of querying links by source in PostgreSQL. +//! +//! ## Implementation +//! +//! SQL query: +//! ```sql +//! SELECT id, source, target FROM links WHERE source = $1 +//! ``` +//! +//! - Source column index lookup +//! - Time complexity: O(log n + k) where k is number of matching links + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::{ + data::{Flow, LinksConstants}, + Doublets, +}; +use linkspsql::{background_links, bench, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the each_outgoing benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let handler = |_| Flow::Continue; + let any = LinksConstants::new().any; + let bg_links = background_links(); + group.bench_function(id, |bencher| { + bench!(|fork| as B { + for index in 1..=bg_links { + let _ = elapsed! {fork.each_by([any, index, any], handler)}; + } + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on querying links by source. +pub fn each_outgoing(c: &mut Criterion) { + let mut group = c.benchmark_group("Each_Outgoing"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/benches/benchmarks/psql/mod.rs b/rust/benches/benchmarks/psql/mod.rs new file mode 100644 index 0000000..ba001f8 --- /dev/null +++ b/rust/benches/benchmarks/psql/mod.rs @@ -0,0 +1,33 @@ +//! # PostgreSQL Benchmark Implementations +//! +//! This module contains all benchmark implementations for PostgreSQL. +//! Each benchmark tests a specific database operation using SQL queries +//! executed via libpq client. +//! +//! ## Benchmarked Operations +//! +//! | Benchmark | SQL Query | +//! |-----------------|-------------------------------------------------------------| +//! | `create_links` | `INSERT INTO links (id, source, target) VALUES ($1, $1, $1)`| +//! | `delete_links` | `DELETE FROM links WHERE id = $1` | +//! | `update_links` | `UPDATE links SET source = $2, target = $3 WHERE id = $1` | +//! | `each_all` | `SELECT id, source, target FROM links` | +//! | `each_identity` | `SELECT id, source, target FROM links WHERE id = $1` | +//! | `each_concrete` | `SELECT ... WHERE source = $1 AND target = $2` | +//! | `each_outgoing` | `SELECT ... WHERE source = $1` | +//! | `each_incoming` | `SELECT ... WHERE target = $1` | +//! +//! ## Storage Backends Tested +//! +//! - `PSQL_NonTransaction` - Direct SQL execution without transaction wrapping +//! - `PSQL_Transaction` - SQL execution within explicit transaction + +mod create; +mod delete; +pub mod each; +mod update; + +pub use create::create_links; +pub use delete::delete_links; +pub use each::*; +pub use update::update_links; diff --git a/rust/benches/benchmarks/psql/update.rs b/rust/benches/benchmarks/psql/update.rs new file mode 100644 index 0000000..81e0208 --- /dev/null +++ b/rust/benches/benchmarks/psql/update.rs @@ -0,0 +1,59 @@ +//! # PostgreSQL Update Links Benchmark +//! +//! This benchmark measures the performance of updating links in PostgreSQL. +//! +//! ## Implementation +//! +//! PostgreSQL executes this SQL query: +//! ```sql +//! UPDATE links SET source = $2, target = $3 WHERE id = $1 +//! ``` +//! +//! - Makes connection to PostgreSQL database +//! - Executes UPDATE statement +//! - Time complexity: O(log n) for B-tree index updates + network overhead + +use std::time::{Duration, Instant}; + +use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; +use doublets::Doublets; +use linkspsql::{background_links, bench, benchmark_links, connect, Benched, Client, Exclusive, Fork, Transaction}; + +use crate::tri; + +/// Runs the update benchmark on a PostgreSQL backend. +fn bench>( + group: &mut BenchmarkGroup, + id: &str, + mut benched: B, +) { + let bg_links = background_links(); + let links = benchmark_links(); + group.bench_function(id, |bencher| { + bench!(|fork| as B { + for id in bg_links - (links - 1)..=bg_links { + let _ = elapsed! {fork.update(id, 0, 0)?}; + let _ = elapsed! {fork.update(id, id, id)?}; + } + })(bencher, &mut benched); + }); +} + +/// Creates benchmark for PostgreSQL backends on link updates. +pub fn update_links(c: &mut Criterion) { + let mut group = c.benchmark_group("Update"); + + tri! { + bench(&mut group, "PSQL_NonTransaction", Exclusive::>::setup(()).unwrap()); + } + tri! { + let mut client = connect().unwrap(); + bench( + &mut group, + "PSQL_Transaction", + Exclusive::>::setup(&mut client).unwrap(), + ); + } + + group.finish(); +} diff --git a/rust/src/benched.rs b/rust/src/benched.rs deleted file mode 100644 index 4857cfd..0000000 --- a/rust/src/benched.rs +++ /dev/null @@ -1,111 +0,0 @@ -use { - crate::{map_file, Client, Exclusive, Fork, Result, Sql, Transaction}, - doublets::{ - data::LinkType, - mem::{Alloc, FileMapped}, - split::{self, DataPart, IndexPart}, - unit::{self, LinkPart}, - Doublets, - }, - std::alloc::Global, -}; - -pub trait Benched: Sized { - type Builder<'params>; - - fn setup<'a>(builder: Self::Builder<'a>) -> Result; - - fn fork(&mut self) -> Fork { - Fork(self) - } - - unsafe fn unfork(&mut self); -} - -// fixme: useless constraints -impl Benched for unit::Store>> { - type Builder<'a> = &'a str; - - fn setup(builder: Self::Builder<'_>) -> Result { - Self::new(map_file(builder)?).map_err(Into::into) - } - - unsafe fn unfork(&mut self) { - let _ = self.delete_all(); - } -} - -impl Benched for unit::Store, Global>> { - type Builder<'a> = (); - - fn setup(_: Self::Builder<'_>) -> Result { - Self::new(Alloc::new(Global)).map_err(Into::into) - } - - unsafe fn unfork(&mut self) { - let _ = self.delete_all(); - } -} - -impl Benched for split::Store>, FileMapped>> { - type Builder<'a> = (&'a str, &'a str); - - fn setup((data, index): Self::Builder<'_>) -> Result { - Self::new(map_file(data)?, map_file(index)?).map_err(Into::into) - } - - unsafe fn unfork(&mut self) { - let _ = self.delete_all(); - } -} - -impl Benched - for split::Store, Global>, Alloc, Global>> -{ - type Builder<'a> = (); - - fn setup(_: Self::Builder<'_>) -> Result { - Self::new(Alloc::new(Global), Alloc::new(Global)).map_err(Into::into) - } - - unsafe fn unfork(&mut self) { - let _ = self.delete_all(); - } -} - -impl Benched for Exclusive> { - type Builder<'a> = (); - - fn setup(_: Self::Builder<'_>) -> Result { - unsafe { Ok(Exclusive::new(crate::connect()?)) } - } - - fn fork(&mut self) -> Fork { - let _ = self.create_table(); - Fork(self) - } - - unsafe fn unfork(&mut self) { - let _ = self.drop_table(); - } -} - -impl<'a, T: LinkType> Benched for Exclusive> { - type Builder<'b> = &'a mut Client; - - fn setup(builder: Self::Builder<'_>) -> Result { - let mut transaction = builder.transaction()?; - transaction.create_table()?; - // Safety: todo - unsafe { Ok(Exclusive::new(transaction)) } - } - - fn fork(&mut self) -> Fork { - let _ = self.create_table(); - Fork(self) - } - - unsafe fn unfork(&mut self) { - let _ = self.drop_table(); - } -} diff --git a/rust/src/benched/doublets_benched.rs b/rust/src/benched/doublets_benched.rs new file mode 100644 index 0000000..63245d3 --- /dev/null +++ b/rust/src/benched/doublets_benched.rs @@ -0,0 +1,84 @@ +//! # Doublets Backend Implementations +//! +//! This module contains [`Benched`] trait implementations for all Doublets +//! storage variants. +//! +//! ## Storage Backends +//! +//! | Backend | Description | +//! |----------------------------------|--------------------------------| +//! | `unit::Store>` | File-mapped unit storage | +//! | `unit::Store>` | In-memory unit storage | +//! | `split::Store>` | File-mapped split storage | +//! | `split::Store>` | In-memory split storage | +//! +//! ## Cleanup Strategy +//! +//! Each backend's `unfork()` implementation calls `delete_all()` to clean up +//! all links created during the benchmark iteration. + +use crate::{map_file, Result}; +use doublets::{ + data::LinkType, + mem::{Alloc, FileMapped}, + split::{self, DataPart, IndexPart}, + unit::{self, LinkPart}, + Doublets, +}; +use std::alloc::Global; + +use super::Benched; + +/// Benched implementation for file-mapped unit storage. +impl Benched for unit::Store>> { + type Builder<'a> = &'a str; + + fn setup(builder: Self::Builder<'_>) -> Result { + Self::new(map_file(builder)?).map_err(Into::into) + } + + unsafe fn unfork(&mut self) { + let _ = self.delete_all(); + } +} + +/// Benched implementation for in-memory unit storage. +impl Benched for unit::Store, Global>> { + type Builder<'a> = (); + + fn setup(_: Self::Builder<'_>) -> Result { + Self::new(Alloc::new(Global)).map_err(Into::into) + } + + unsafe fn unfork(&mut self) { + let _ = self.delete_all(); + } +} + +/// Benched implementation for file-mapped split storage. +impl Benched for split::Store>, FileMapped>> { + type Builder<'a> = (&'a str, &'a str); + + fn setup((data, index): Self::Builder<'_>) -> Result { + Self::new(map_file(data)?, map_file(index)?).map_err(Into::into) + } + + unsafe fn unfork(&mut self) { + let _ = self.delete_all(); + } +} + +/// Benched implementation for in-memory split storage. +impl Benched + for split::Store, Global>, Alloc, Global>> +{ + type Builder<'a> = (); + + fn setup(_: Self::Builder<'_>) -> Result { + Self::new(Alloc::new(Global), Alloc::new(Global)).map_err(Into::into) + } + + unsafe fn unfork(&mut self) { + let _ = self.delete_all(); + } +} diff --git a/rust/src/benched/mod.rs b/rust/src/benched/mod.rs new file mode 100644 index 0000000..02db6e7 --- /dev/null +++ b/rust/src/benched/mod.rs @@ -0,0 +1,44 @@ +//! # Benchmark Lifecycle Management +//! +//! This module defines the [`Benched`] trait that provides setup/teardown lifecycle +//! for benchmark iterations. Both PostgreSQL and Doublets storage backends implement +//! this trait to enable fair benchmarking. +//! +//! ## Module Structure +//! +//! The implementations are split into separate files for clear comparison: +//! +//! - **[`doublets_benched`]** - Doublets storage backend implementations +//! - **[`psql_benched`]** - PostgreSQL storage backend implementations + +mod doublets_benched; +mod psql_benched; + +use crate::Fork; + +/// Trait for types that can be benchmarked. +/// +/// Provides the setup/teardown lifecycle for benchmark iterations: +/// - [`Benched::setup`] - Initialize the storage backend +/// - [`Benched::fork`] - Create an isolated environment for a single iteration +/// - [`Benched::unfork`] - Clean up after the iteration +pub trait Benched: Sized { + /// Builder parameter type for constructing this storage. + type Builder<'params>; + + /// Set up a new storage backend for benchmarking. + fn setup<'a>(builder: Self::Builder<'a>) -> crate::Result; + + /// Create a fork for a single benchmark iteration. + /// + /// This allows each iteration to run in isolation without affecting others. + fn fork(&mut self) -> Fork { + Fork(self) + } + + /// Clean up after a benchmark iteration. + /// + /// # Safety + /// This method may perform unsafe operations like clearing all data. + unsafe fn unfork(&mut self); +} diff --git a/rust/src/benched/psql_benched.rs b/rust/src/benched/psql_benched.rs new file mode 100644 index 0000000..098e962 --- /dev/null +++ b/rust/src/benched/psql_benched.rs @@ -0,0 +1,73 @@ +//! # PostgreSQL Backend Implementations +//! +//! This module contains [`Benched`] trait implementations for PostgreSQL +//! storage variants. +//! +//! ## Storage Backends +//! +//! | Backend | Description | +//! |----------------------------|-------------------------------------| +//! | `Exclusive>` | Direct SQL execution (no transaction) | +//! | `Exclusive>`| SQL execution within transaction | +//! +//! ## Table Management +//! +//! Each backend's lifecycle manages a `links` table: +//! - `fork()` calls `CREATE TABLE` to set up the table +//! - `unfork()` calls `DROP TABLE` to clean up +//! +//! ## SQL Schema +//! +//! The `links` table has the following structure: +//! ```sql +//! CREATE TABLE links ( +//! id BIGINT PRIMARY KEY, +//! source BIGINT NOT NULL, +//! target BIGINT NOT NULL +//! ) +//! ``` + +use crate::{Client, Exclusive, Fork, Result, Sql, Transaction}; +use doublets::data::LinkType; + +use super::Benched; + +/// Benched implementation for PostgreSQL client (non-transactional). +impl Benched for Exclusive> { + type Builder<'a> = (); + + fn setup(_: Self::Builder<'_>) -> Result { + // Safety: Exclusive wrapper ensures single-threaded access + unsafe { Ok(Exclusive::new(crate::connect()?)) } + } + + fn fork(&mut self) -> Fork { + let _ = self.create_table(); + Fork(self) + } + + unsafe fn unfork(&mut self) { + let _ = self.drop_table(); + } +} + +/// Benched implementation for PostgreSQL transaction. +impl<'a, T: LinkType> Benched for Exclusive> { + type Builder<'b> = &'a mut Client; + + fn setup(builder: Self::Builder<'_>) -> Result { + let mut transaction = builder.transaction()?; + transaction.create_table()?; + // Safety: Exclusive wrapper ensures single-threaded access + unsafe { Ok(Exclusive::new(transaction)) } + } + + fn fork(&mut self) -> Fork { + let _ = self.create_table(); + Fork(self) + } + + unsafe fn unfork(&mut self) { + let _ = self.drop_table(); + } +}