From 3ad540103faa064b680dc5492dbb4de82b914c80 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 12 Jan 2026 22:42:01 +0100 Subject: [PATCH 1/9] Initial commit with task details Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/Comparisons.SQLiteVSDoublets/issues/103 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f07483e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/Comparisons.SQLiteVSDoublets/issues/103 +Your prepared branch: issue-103-3cc61f722359 +Your prepared working directory: /tmp/gh-issue-solver-1768254119878 + +Proceed. From 4e88bf1c271dc66d89c075a29f6a555de29923ca Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 12 Jan 2026 23:29:16 +0100 Subject: [PATCH 2/9] Add Rust implementation of SQLite vs Doublets benchmark This commit introduces a complete Rust benchmark suite comparing SQLite and Doublets performance for basic CRUD operations (Create, Read, Update, Delete). Implementation details: - SQLite: Uses rusqlite with in-memory database (:memory:) - Doublets: Uses doublets-rs with volatile (in-memory) storage - Both United and Split store variants supported - Benchmarks use Criterion for statistical analysis - All benchmarks use configurable link counts via environment variables Benchmark groups: - create: Point creation performance - delete: Link deletion performance - update: Link update (source/target modification) - query_all: Full table scan - query_by_id: Primary key lookup - query_by_source: Index-based query by source - query_by_target: Index-based query by target Note: Uses nightly-2022-08-22 toolchain for doublets-rs compatibility. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/rust.yml | 79 +++ rust/.gitignore | 3 + rust/Cargo.lock | 920 +++++++++++++++++++++++++++ rust/Cargo.toml | 34 + rust/benches/bench.rs | 381 +++++++++++ rust/rust-toolchain.toml | 3 + rust/rustfmt.toml | 2 + rust/src/benched/doublets_benched.rs | 81 +++ rust/src/benched/mod.rs | 27 + rust/src/benched/sqlite_benched.rs | 41 ++ rust/src/doublets_impl.rs | 186 ++++++ rust/src/exclusive.rs | 46 ++ rust/src/fork.rs | 40 ++ rust/src/lib.rs | 99 +++ rust/src/sqlite_impl.rs | 297 +++++++++ 15 files changed, 2239 insertions(+) create mode 100644 .github/workflows/rust.yml create mode 100644 rust/.gitignore create mode 100644 rust/Cargo.lock create mode 100644 rust/Cargo.toml create mode 100644 rust/benches/bench.rs create mode 100644 rust/rust-toolchain.toml create mode 100644 rust/rustfmt.toml create mode 100644 rust/src/benched/doublets_benched.rs create mode 100644 rust/src/benched/mod.rs create mode 100644 rust/src/benched/sqlite_benched.rs create mode 100644 rust/src/doublets_impl.rs create mode 100644 rust/src/exclusive.rs create mode 100644 rust/src/fork.rs create mode 100644 rust/src/lib.rs create mode 100644 rust/src/sqlite_impl.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..cf754a5 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,79 @@ +name: Rust + +on: + push: + branches: main + paths: + - 'rust/**' + - '.github/workflows/rust.yml' + pull_request: + paths: + - 'rust/**' + - '.github/workflows/rust.yml' + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +defaults: + run: + working-directory: rust + +jobs: + test: + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2022-08-22 + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + rust/target + key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Run tests + run: cargo test --release + + benchmark: + name: Benchmark + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2022-08-22 + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + rust/target + key: ${{ runner.os }}-cargo-bench-${{ hashFiles('rust/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-bench- + + - name: Run benchmarks + run: cargo bench -- --noplot + env: + BENCHMARK_LINK_COUNT: 1000 + BACKGROUND_LINK_COUNT: 3000 diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..944611a --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,3 @@ +/target/ +*.db +*.links diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..b2de794 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,920 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags 1.3.2", + "textwrap", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "delegate" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d70a2d4995466955a415223acf3c9c934b9ff2339631cdf4ffc893da4bacd717" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "doublets" +version = "0.1.0-pre+beta.15" +source = "git+https://github.com/linksplatform/doublets-rs.git?rev=5522d91c536654934b7181829f6efb570fb8bb44#5522d91c536654934b7181829f6efb570fb8bb44" +dependencies = [ + "bumpalo", + "cfg-if", + "leak_slice", + "platform-data", + "platform-mem", + "platform-trees", + "tap", + "thiserror", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leak_slice" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf3387da9fb41906394e1306ddd3cd26dd9b7177af11c19b45b364b743aed26" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libsqlite3-sys" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "platform-data" +version = "0.1.0-beta.3" +source = "git+https://github.com/linksplatform/doublets-rs.git?rev=5522d91c536654934b7181829f6efb570fb8bb44#5522d91c536654934b7181829f6efb570fb8bb44" +dependencies = [ + "beef", + "funty", + "thiserror", +] + +[[package]] +name = "platform-mem" +version = "0.1.0-pre+beta.2" +source = "git+https://github.com/linksplatform/doublets-rs.git?rev=5522d91c536654934b7181829f6efb570fb8bb44#5522d91c536654934b7181829f6efb570fb8bb44" +dependencies = [ + "delegate", + "memmap2", + "tap", + "tempfile", + "thiserror", +] + +[[package]] +name = "platform-trees" +version = "0.1.0-beta.1" +source = "git+https://github.com/linksplatform/doublets-rs.git?rev=5522d91c536654934b7181829f6efb570fb8bb44#5522d91c536654934b7181829f6efb570fb8bb44" +dependencies = [ + "funty", + "platform-data", +] + +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rayon" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rusqlite" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a" +dependencies = [ + "bitflags 1.3.2", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "memchr", + "smallvec", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.185" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "sqlite-vs-doublets" +version = "0.1.0" +dependencies = [ + "bumpalo", + "criterion", + "doublets", + "itoa", + "once_cell", + "proc-macro2", + "quote", + "rayon", + "rayon-core", + "rusqlite", + "ryu", + "serde_derive", + "syn", + "unicode-width", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..71fff04 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "sqlite-vs-doublets" +version = "0.1.0" +edition = "2021" +license = "Unlicense" +description = "Benchmark comparing SQLite vs Doublets performance for basic CRUD operations" + +[[bench]] +name = "bench" +harness = false + +[dependencies] +rusqlite = { version = "=0.27.0", features = ["bundled"] } +doublets = { git = "https://github.com/linksplatform/doublets-rs.git", rev = "5522d91c536654934b7181829f6efb570fb8bb44" } +once_cell = "1.14" + +[dev-dependencies] +criterion = { version = "=0.3.6", default-features = false } + +# Pin dependencies for compatibility with nightly-2022-08-22 (rustc 1.65) +serde_derive = "=1.0.136" +proc-macro2 = "=1.0.36" +quote = "=1.0.15" +syn = "=1.0.86" +bumpalo = "=3.11.1" +rayon = "=1.6.1" +rayon-core = "=1.10.2" +itoa = "=1.0.5" +ryu = "=1.0.12" +unicode-width = "=0.1.10" + +[profile.release] +lto = true +codegen-units = 1 diff --git a/rust/benches/bench.rs b/rust/benches/bench.rs new file mode 100644 index 0000000..8a17a1c --- /dev/null +++ b/rust/benches/bench.rs @@ -0,0 +1,381 @@ +//! Benchmark suite comparing SQLite vs Doublets performance +//! +//! This benchmark measures basic CRUD operations with links on both storage systems. + +use criterion::{criterion_group, criterion_main, Criterion, Throughput}; +use sqlite_vs_doublets::{ + benched::{DoubletsSplitVolatileBenched, DoubletsUnitedVolatileBenched, SqliteMemoryBenched}, + Benched, Links, BACKGROUND_LINK_COUNT, BENCHMARK_LINK_COUNT, +}; + +/// Run create operations benchmark +fn bench_create(c: &mut Criterion) { + let mut group = c.benchmark_group("create"); + let link_count = *BENCHMARK_LINK_COUNT; + group.throughput(Throughput::Elements(link_count as u64)); + + group.bench_function("SQLite_Memory", |b| { + let mut benched = SqliteMemoryBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + // Create background links + for _ in 0..*BACKGROUND_LINK_COUNT { + fork.create_point(); + } + // Benchmark operation + for _ in 0..link_count { + fork.create_point(); + } + }); + }); + + group.bench_function("Doublets_United_Volatile", |b| { + let mut benched = DoubletsUnitedVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for _ in 0..*BACKGROUND_LINK_COUNT { + fork.create_point(); + } + for _ in 0..link_count { + fork.create_point(); + } + }); + }); + + group.bench_function("Doublets_Split_Volatile", |b| { + let mut benched = DoubletsSplitVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for _ in 0..*BACKGROUND_LINK_COUNT { + fork.create_point(); + } + for _ in 0..link_count { + fork.create_point(); + } + }); + }); + + group.finish(); +} + +/// Run delete operations benchmark +fn bench_delete(c: &mut Criterion) { + let mut group = c.benchmark_group("delete"); + let link_count = *BENCHMARK_LINK_COUNT; + group.throughput(Throughput::Elements(link_count as u64)); + + group.bench_function("SQLite_Memory", |b| { + let mut benched = SqliteMemoryBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + // Create links to delete + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + // Benchmark delete + for id in ids { + fork.delete(id); + } + }); + }); + + group.bench_function("Doublets_United_Volatile", |b| { + let mut benched = DoubletsUnitedVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + for id in ids { + fork.delete(id); + } + }); + }); + + group.bench_function("Doublets_Split_Volatile", |b| { + let mut benched = DoubletsSplitVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + for id in ids { + fork.delete(id); + } + }); + }); + + group.finish(); +} + +/// Run update operations benchmark +fn bench_update(c: &mut Criterion) { + let mut group = c.benchmark_group("update"); + let link_count = *BENCHMARK_LINK_COUNT; + group.throughput(Throughput::Elements(link_count as u64)); + + group.bench_function("SQLite_Memory", |b| { + let mut benched = SqliteMemoryBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + // Create links to update + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + // Benchmark update + for (i, id) in ids.iter().enumerate() { + fork.update(*id, (i + 1) as u64, (i + 2) as u64); + } + }); + }); + + group.bench_function("Doublets_United_Volatile", |b| { + let mut benched = DoubletsUnitedVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + for (i, id) in ids.iter().enumerate() { + fork.update(*id, (i + 1) as u64, (i + 2) as u64); + } + }); + }); + + group.bench_function("Doublets_Split_Volatile", |b| { + let mut benched = DoubletsSplitVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + for (i, id) in ids.iter().enumerate() { + fork.update(*id, (i + 1) as u64, (i + 2) as u64); + } + }); + }); + + group.finish(); +} + +/// Run query all benchmark +fn bench_query_all(c: &mut Criterion) { + let mut group = c.benchmark_group("query_all"); + let link_count = *BENCHMARK_LINK_COUNT; + group.throughput(Throughput::Elements(link_count as u64)); + + group.bench_function("SQLite_Memory", |b| { + let mut benched = SqliteMemoryBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + // Create links to query + for _ in 0..link_count { + fork.create_point(); + } + // Benchmark query + let _ = fork.query_all(); + }); + }); + + group.bench_function("Doublets_United_Volatile", |b| { + let mut benched = DoubletsUnitedVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for _ in 0..link_count { + fork.create_point(); + } + let _ = fork.query_all(); + }); + }); + + group.bench_function("Doublets_Split_Volatile", |b| { + let mut benched = DoubletsSplitVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for _ in 0..link_count { + fork.create_point(); + } + let _ = fork.query_all(); + }); + }); + + group.finish(); +} + +/// Run query by ID benchmark +fn bench_query_by_id(c: &mut Criterion) { + let mut group = c.benchmark_group("query_by_id"); + let link_count = *BENCHMARK_LINK_COUNT; + group.throughput(Throughput::Elements(link_count as u64)); + + group.bench_function("SQLite_Memory", |b| { + let mut benched = SqliteMemoryBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + // Create links + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + // Benchmark query + for id in &ids { + let _ = fork.query_by_id(*id); + } + }); + }); + + group.bench_function("Doublets_United_Volatile", |b| { + let mut benched = DoubletsUnitedVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + for id in &ids { + let _ = fork.query_by_id(*id); + } + }); + }); + + group.bench_function("Doublets_Split_Volatile", |b| { + let mut benched = DoubletsSplitVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + let mut ids = Vec::with_capacity(link_count); + for _ in 0..link_count { + ids.push(fork.create_point()); + } + for id in &ids { + let _ = fork.query_by_id(*id); + } + }); + }); + + group.finish(); +} + +/// Run query by source benchmark +fn bench_query_by_source(c: &mut Criterion) { + let mut group = c.benchmark_group("query_by_source"); + let link_count = *BENCHMARK_LINK_COUNT; + group.throughput(Throughput::Elements(link_count as u64)); + + group.bench_function("SQLite_Memory", |b| { + let mut benched = SqliteMemoryBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + // Create links with various sources + for i in 0..link_count { + let id = fork.create_point(); + fork.update(id, (i % 10 + 1) as u64, id); + } + // Benchmark query + for i in 1..=10 { + let _ = fork.query_by_source(i as u64); + } + }); + }); + + group.bench_function("Doublets_United_Volatile", |b| { + let mut benched = DoubletsUnitedVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for i in 0..link_count { + let id = fork.create_point(); + fork.update(id, (i % 10 + 1) as u64, id); + } + for i in 1..=10 { + let _ = fork.query_by_source(i as u64); + } + }); + }); + + group.bench_function("Doublets_Split_Volatile", |b| { + let mut benched = DoubletsSplitVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for i in 0..link_count { + let id = fork.create_point(); + fork.update(id, (i % 10 + 1) as u64, id); + } + for i in 1..=10 { + let _ = fork.query_by_source(i as u64); + } + }); + }); + + group.finish(); +} + +/// Run query by target benchmark +fn bench_query_by_target(c: &mut Criterion) { + let mut group = c.benchmark_group("query_by_target"); + let link_count = *BENCHMARK_LINK_COUNT; + group.throughput(Throughput::Elements(link_count as u64)); + + group.bench_function("SQLite_Memory", |b| { + let mut benched = SqliteMemoryBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + // Create links with various targets + for i in 0..link_count { + let id = fork.create_point(); + fork.update(id, id, (i % 10 + 1) as u64); + } + // Benchmark query + for i in 1..=10 { + let _ = fork.query_by_target(i as u64); + } + }); + }); + + group.bench_function("Doublets_United_Volatile", |b| { + let mut benched = DoubletsUnitedVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for i in 0..link_count { + let id = fork.create_point(); + fork.update(id, id, (i % 10 + 1) as u64); + } + for i in 1..=10 { + let _ = fork.query_by_target(i as u64); + } + }); + }); + + group.bench_function("Doublets_Split_Volatile", |b| { + let mut benched = DoubletsSplitVolatileBenched::setup(()); + b.iter(|| { + let mut fork = benched.fork(); + for i in 0..link_count { + let id = fork.create_point(); + fork.update(id, id, (i % 10 + 1) as u64); + } + for i in 1..=10 { + let _ = fork.query_by_target(i as u64); + } + }); + }); + + group.finish(); +} + +criterion_group!( + benches, + bench_create, + bench_delete, + bench_update, + bench_query_all, + bench_query_by_id, + bench_query_by_source, + bench_query_by_target, +); + +criterion_main!(benches); diff --git a/rust/rust-toolchain.toml b/rust/rust-toolchain.toml new file mode 100644 index 0000000..33c06c9 --- /dev/null +++ b/rust/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2022-08-22" +components = ["rustfmt", "clippy"] diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml new file mode 100644 index 0000000..79f8a99 --- /dev/null +++ b/rust/rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2021" +tab_spaces = 4 diff --git a/rust/src/benched/doublets_benched.rs b/rust/src/benched/doublets_benched.rs new file mode 100644 index 0000000..a1613f2 --- /dev/null +++ b/rust/src/benched/doublets_benched.rs @@ -0,0 +1,81 @@ +//! Benched implementations for Doublets + +use crate::{Benched, Fork, Links}; +use crate::doublets_impl::{ + create_split_volatile, create_united_volatile, DoubletsLinks, DoubletsSplitVolatile, + DoubletsUnitedVolatile, +}; + +/// Benched implementation for Doublets United (unit) store with volatile storage +pub struct DoubletsUnitedVolatileBenched { + links: DoubletsLinks, +} + +impl Benched for DoubletsUnitedVolatileBenched { + type Builder = (); + + fn setup(_builder: Self::Builder) -> Self { + Self { + links: create_united_volatile(), + } + } + + fn fork(&mut self) -> Fork { + Fork::new(self) + } + + unsafe fn unfork(&mut self) { + self.links.delete_all(); + } +} + +impl std::ops::Deref for DoubletsUnitedVolatileBenched { + type Target = DoubletsLinks; + + fn deref(&self) -> &Self::Target { + &self.links + } +} + +impl std::ops::DerefMut for DoubletsUnitedVolatileBenched { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.links + } +} + +/// Benched implementation for Doublets Split store with volatile storage +pub struct DoubletsSplitVolatileBenched { + links: DoubletsLinks, +} + +impl Benched for DoubletsSplitVolatileBenched { + type Builder = (); + + fn setup(_builder: Self::Builder) -> Self { + Self { + links: create_split_volatile(), + } + } + + fn fork(&mut self) -> Fork { + Fork::new(self) + } + + unsafe fn unfork(&mut self) { + self.links.delete_all(); + } +} + +impl std::ops::Deref for DoubletsSplitVolatileBenched { + type Target = DoubletsLinks; + + fn deref(&self) -> &Self::Target { + &self.links + } +} + +impl std::ops::DerefMut for DoubletsSplitVolatileBenched { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.links + } +} diff --git a/rust/src/benched/mod.rs b/rust/src/benched/mod.rs new file mode 100644 index 0000000..3e5be02 --- /dev/null +++ b/rust/src/benched/mod.rs @@ -0,0 +1,27 @@ +//! Benched trait and implementations for SQLite and Doublets + +mod doublets_benched; +mod sqlite_benched; + +pub use doublets_benched::*; +pub use sqlite_benched::*; + +use crate::Fork; + +/// Trait for types that can be benchmarked with setup/teardown lifecycle +pub trait Benched: Sized { + /// The builder type used to construct this benched type + type Builder; + + /// Setup the benchmark environment + fn setup(builder: Self::Builder) -> Self; + + /// Create a fork for an isolated benchmark iteration + fn fork(&mut self) -> Fork; + + /// Clean up after a benchmark iteration + /// + /// # Safety + /// This should only be called by Fork's Drop implementation + unsafe fn unfork(&mut self); +} diff --git a/rust/src/benched/sqlite_benched.rs b/rust/src/benched/sqlite_benched.rs new file mode 100644 index 0000000..bc9caa6 --- /dev/null +++ b/rust/src/benched/sqlite_benched.rs @@ -0,0 +1,41 @@ +//! Benched implementation for SQLite + +use crate::{Benched, Fork}; +use crate::sqlite_impl::SqliteLinks; + +/// Benched implementation for SQLite with in-memory database +pub struct SqliteMemoryBenched { + links: SqliteLinks, +} + +impl Benched for SqliteMemoryBenched { + type Builder = (); + + fn setup(_builder: Self::Builder) -> Self { + Self { + links: SqliteLinks::new_memory(), + } + } + + fn fork(&mut self) -> Fork { + Fork::new(self) + } + + unsafe fn unfork(&mut self) { + self.links.reset(); + } +} + +impl std::ops::Deref for SqliteMemoryBenched { + type Target = SqliteLinks; + + fn deref(&self) -> &Self::Target { + &self.links + } +} + +impl std::ops::DerefMut for SqliteMemoryBenched { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.links + } +} diff --git a/rust/src/doublets_impl.rs b/rust/src/doublets_impl.rs new file mode 100644 index 0000000..9cb2760 --- /dev/null +++ b/rust/src/doublets_impl.rs @@ -0,0 +1,186 @@ +//! Doublets storage implementation for links + +use crate::{Link, Links}; +use doublets::{ + mem::Alloc, + split::{self, DataPart, IndexPart}, + unit::{self, LinkPart}, + Doublets, DoubletsExt, +}; +use std::alloc::Global; + +/// Type alias for Doublets united (unit) store with volatile (in-memory) storage. +/// Each link is stored as a contiguous unit containing (id, source, target). +pub type DoubletsUnitedVolatile = unit::Store, Global>>; + +/// Type alias for Doublets split store with volatile (in-memory) storage. +/// Separates data and index into different memory regions for better cache efficiency. +pub type DoubletsSplitVolatile = + split::Store, Global>, Alloc, Global>>; + +/// Wrapper to adapt doublets::Doublets to our Links trait +pub struct DoubletsLinks { + store: S, +} + +impl DoubletsLinks { + pub fn new(store: S) -> Self { + Self { store } + } + + pub fn into_inner(self) -> S { + self.store + } +} + +impl + DoubletsExt> Links for DoubletsLinks { + fn create(&mut self, source: u64, target: u64) -> u64 { + self.store + .create_by([source as usize, target as usize]) + .expect("Failed to create link") as u64 + } + + fn create_point(&mut self) -> u64 { + self.store.create_point().expect("Failed to create point") as u64 + } + + fn update(&mut self, id: u64, source: u64, target: u64) { + self.store + .update(id as usize, source as usize, target as usize) + .expect("Failed to update link"); + } + + fn delete(&mut self, id: u64) { + self.store + .delete(id as usize) + .expect("Failed to delete link"); + } + + fn delete_all(&mut self) { + let any = self.store.constants().any; + let ids: Vec = self + .store + .each_iter([any, any, any]) + .map(|link| link.index) + .collect(); + for id in ids { + let _ = self.store.delete(id); + } + } + + fn query_all(&self) -> Vec { + let any = self.store.constants().any; + self.store + .each_iter([any, any, any]) + .map(|link| Link::new(link.index as u64, link.source as u64, link.target as u64)) + .collect() + } + + fn query_by_id(&self, id: u64) -> Option { + self.store.get_link(id as usize).map(|link| { + Link::new(link.index as u64, link.source as u64, link.target as u64) + }) + } + + fn query_by_source(&self, source: u64) -> Vec { + let any = self.store.constants().any; + self.store + .each_iter([any, source as usize, any]) + .map(|link| Link::new(link.index as u64, link.source as u64, link.target as u64)) + .collect() + } + + fn query_by_target(&self, target: u64) -> Vec { + let any = self.store.constants().any; + self.store + .each_iter([any, any, target as usize]) + .map(|link| Link::new(link.index as u64, link.source as u64, link.target as u64)) + .collect() + } + + fn query_by_source_target(&self, source: u64, target: u64) -> Vec { + let any = self.store.constants().any; + self.store + .each_iter([any, source as usize, target as usize]) + .map(|link| Link::new(link.index as u64, link.source as u64, link.target as u64)) + .collect() + } + + fn count(&self) -> usize { + self.store.count() + } +} + +/// Create a new in-memory doublets united store +pub fn create_united_volatile() -> DoubletsLinks { + let mem = Alloc::new(Global); + let store = DoubletsUnitedVolatile::new(mem).expect("Failed to create doublets store"); + DoubletsLinks::new(store) +} + +/// Create a new in-memory doublets split store +pub fn create_split_volatile() -> DoubletsLinks { + let data_mem = Alloc::new(Global); + let index_mem = Alloc::new(Global); + let store = + DoubletsSplitVolatile::new(data_mem, index_mem).expect("Failed to create doublets store"); + DoubletsLinks::new(store) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_and_query_united() { + let mut db = create_united_volatile(); + let id = db.create_point(); + assert_eq!(id, 1); + + let link = db.query_by_id(id).unwrap(); + assert_eq!(link.source, id); + assert_eq!(link.target, id); + } + + #[test] + fn test_create_and_query_split() { + let mut db = create_split_volatile(); + let id = db.create_point(); + assert_eq!(id, 1); + + let link = db.query_by_id(id).unwrap(); + assert_eq!(link.source, id); + assert_eq!(link.target, id); + } + + #[test] + fn test_update() { + let mut db = create_united_volatile(); + let id = db.create(1, 2); + db.update(id, 3, 4); + + let link = db.query_by_id(id).unwrap(); + assert_eq!(link.source, 3); + assert_eq!(link.target, 4); + } + + #[test] + fn test_delete() { + let mut db = create_united_volatile(); + let id = db.create_point(); + db.delete(id); + assert!(db.query_by_id(id).is_none()); + } + + #[test] + fn test_query_by_source() { + let mut db = create_united_volatile(); + let id1 = db.create_point(); + let id2 = db.create_point(); + db.update(id1, id1, id2); + db.update(id2, id1, id1); + + let links = db.query_by_source(id1); + assert_eq!(links.len(), 2); + } +} diff --git a/rust/src/exclusive.rs b/rust/src/exclusive.rs new file mode 100644 index 0000000..727f428 --- /dev/null +++ b/rust/src/exclusive.rs @@ -0,0 +1,46 @@ +//! Thread-safe exclusive access wrapper + +use std::cell::UnsafeCell; +use std::ops::{Deref, DerefMut}; + +/// A wrapper that provides mutable access through an immutable reference. +/// This is useful for benchmark scenarios where we need interior mutability. +pub struct Exclusive { + inner: UnsafeCell, +} + +impl Exclusive { + /// Creates a new Exclusive wrapper + pub fn new(value: T) -> Self { + Self { + inner: UnsafeCell::new(value), + } + } + + /// Gets a mutable reference to the inner value + /// + /// # Safety + /// The caller must ensure that no other references exist to the inner value. + #[allow(clippy::mut_from_ref)] + pub fn get_mut(&self) -> &mut T { + unsafe { &mut *self.inner.get() } + } +} + +impl Deref for Exclusive { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.inner.get() } + } +} + +impl DerefMut for Exclusive { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.inner.get() } + } +} + +// SAFETY: We ensure exclusive access through the API design +unsafe impl Send for Exclusive {} +unsafe impl Sync for Exclusive {} diff --git a/rust/src/fork.rs b/rust/src/fork.rs new file mode 100644 index 0000000..ba14355 --- /dev/null +++ b/rust/src/fork.rs @@ -0,0 +1,40 @@ +//! Fork mechanism for isolated benchmark runs + +use crate::Benched; +use std::ops::{Deref, DerefMut}; + +/// A fork that provides an isolated environment for benchmark iterations. +/// When dropped, it calls unfork() to clean up the state. +pub struct Fork<'a, B: Benched> { + benched: &'a mut B, +} + +impl<'a, B: Benched> Fork<'a, B> { + /// Creates a new Fork from a Benched implementation + pub fn new(benched: &'a mut B) -> Self { + Self { benched } + } +} + +impl<'a, B: Benched> Deref for Fork<'a, B> { + type Target = B; + + fn deref(&self) -> &Self::Target { + self.benched + } +} + +impl<'a, B: Benched> DerefMut for Fork<'a, B> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.benched + } +} + +impl<'a, B: Benched> Drop for Fork<'a, B> { + fn drop(&mut self) { + // SAFETY: We ensure the benched is properly cleaned up + unsafe { + self.benched.unfork(); + } + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..04f91f4 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,99 @@ +//! SQLite vs Doublets benchmark library +//! +//! This library provides implementations for benchmarking SQLite and Doublets +//! storage systems on basic CRUD operations with links. + +#![feature(allocator_api)] + +pub mod benched; +pub mod doublets_impl; +pub mod exclusive; +pub mod fork; +pub mod sqlite_impl; + +pub use benched::Benched; +pub use exclusive::Exclusive; +pub use fork::Fork; + +use once_cell::sync::Lazy; +use std::env; + +/// Number of links to use for benchmarking +pub static BENCHMARK_LINK_COUNT: Lazy = Lazy::new(|| { + env::var("BENCHMARK_LINK_COUNT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(1000) +}); + +/// Number of background links to create before benchmarking +pub static BACKGROUND_LINK_COUNT: Lazy = Lazy::new(|| { + env::var("BACKGROUND_LINK_COUNT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(3000) +}); + +/// A link structure representing a doublet (source -> target relationship) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Link { + pub id: u64, + pub source: u64, + pub target: u64, +} + +impl Link { + pub fn new(id: u64, source: u64, target: u64) -> Self { + Self { id, source, target } + } +} + +/// Trait for database operations on links +pub trait Links { + /// Create a new link and return its ID + fn create(&mut self, source: u64, target: u64) -> u64; + + /// Create a point link (self-referencing link) + fn create_point(&mut self) -> u64 { + let id = self.create(0, 0); + self.update(id, id, id); + id + } + + /// Update an existing link + fn update(&mut self, id: u64, source: u64, target: u64); + + /// Delete a link by ID + fn delete(&mut self, id: u64); + + /// Delete all links + fn delete_all(&mut self); + + /// Query all links + fn query_all(&self) -> Vec; + + /// Query a link by ID + fn query_by_id(&self, id: u64) -> Option; + + /// Query links by source + fn query_by_source(&self, source: u64) -> Vec; + + /// Query links by target + fn query_by_target(&self, target: u64) -> Vec; + + /// Query links by source and target + fn query_by_source_target(&self, source: u64, target: u64) -> Vec; + + /// Count all links + fn count(&self) -> usize; +} + +/// Macro for running benchmarks with proper setup and teardown +#[macro_export] +macro_rules! bench { + ($name:expr, $benched:expr, $op:expr) => {{ + use $crate::Benched; + let mut fork = $benched.fork(); + $op(&mut *fork); + }}; +} diff --git a/rust/src/sqlite_impl.rs b/rust/src/sqlite_impl.rs new file mode 100644 index 0000000..1f0d92b --- /dev/null +++ b/rust/src/sqlite_impl.rs @@ -0,0 +1,297 @@ +//! SQLite implementation for links storage + +use crate::{Link, Links}; +use rusqlite::{params, Connection}; +use std::path::Path; + +/// SQLite-based links storage +pub struct SqliteLinks { + conn: Connection, + next_id: u64, +} + +impl SqliteLinks { + /// Create a new SQLite links storage with in-memory database + pub fn new_memory() -> Self { + let conn = Connection::open_in_memory().expect("Failed to open in-memory SQLite database"); + Self::init(conn) + } + + /// Create a new SQLite links storage with file-based database + pub fn new_file>(path: P) -> Self { + let conn = Connection::open(path).expect("Failed to open SQLite database file"); + Self::init(conn) + } + + fn init(conn: Connection) -> Self { + conn.execute( + "CREATE TABLE IF NOT EXISTS links ( + id INTEGER PRIMARY KEY, + source INTEGER NOT NULL, + target INTEGER NOT NULL + )", + [], + ) + .expect("Failed to create links table"); + + // Create indexes for efficient queries + conn.execute( + "CREATE INDEX IF NOT EXISTS idx_source ON links(source)", + [], + ) + .expect("Failed to create source index"); + + conn.execute( + "CREATE INDEX IF NOT EXISTS idx_target ON links(target)", + [], + ) + .expect("Failed to create target index"); + + conn.execute( + "CREATE INDEX IF NOT EXISTS idx_source_target ON links(source, target)", + [], + ) + .expect("Failed to create source_target index"); + + // Get the max ID to continue from + let next_id: u64 = conn + .query_row("SELECT COALESCE(MAX(id), 0) + 1 FROM links", [], |row| { + row.get(0) + }) + .unwrap_or(1); + + Self { conn, next_id } + } + + /// Drop all tables and recreate them + pub fn reset(&mut self) { + self.conn + .execute("DROP TABLE IF EXISTS links", []) + .expect("Failed to drop links table"); + self.conn + .execute( + "CREATE TABLE links ( + id INTEGER PRIMARY KEY, + source INTEGER NOT NULL, + target INTEGER NOT NULL + )", + [], + ) + .expect("Failed to create links table"); + + self.conn + .execute("CREATE INDEX idx_source ON links(source)", []) + .expect("Failed to create source index"); + + self.conn + .execute("CREATE INDEX idx_target ON links(target)", []) + .expect("Failed to create target index"); + + self.conn + .execute( + "CREATE INDEX idx_source_target ON links(source, target)", + [], + ) + .expect("Failed to create source_target index"); + + self.next_id = 1; + } +} + +impl Links for SqliteLinks { + fn create(&mut self, source: u64, target: u64) -> u64 { + let id = self.next_id; + self.conn + .execute( + "INSERT INTO links (id, source, target) VALUES (?1, ?2, ?3)", + params![id as i64, source as i64, target as i64], + ) + .expect("Failed to insert link"); + self.next_id += 1; + id + } + + fn update(&mut self, id: u64, source: u64, target: u64) { + self.conn + .execute( + "UPDATE links SET source = ?1, target = ?2 WHERE id = ?3", + params![source as i64, target as i64, id as i64], + ) + .expect("Failed to update link"); + } + + fn delete(&mut self, id: u64) { + self.conn + .execute("DELETE FROM links WHERE id = ?1", params![id as i64]) + .expect("Failed to delete link"); + } + + fn delete_all(&mut self) { + self.conn + .execute("DELETE FROM links", []) + .expect("Failed to delete all links"); + self.next_id = 1; + } + + fn query_all(&self) -> Vec { + let mut stmt = self + .conn + .prepare("SELECT id, source, target FROM links") + .expect("Failed to prepare query"); + + stmt.query_map([], |row| { + Ok(Link { + id: row.get::<_, i64>(0)? as u64, + source: row.get::<_, i64>(1)? as u64, + target: row.get::<_, i64>(2)? as u64, + }) + }) + .expect("Failed to query links") + .filter_map(|r| r.ok()) + .collect() + } + + fn query_by_id(&self, id: u64) -> Option { + self.conn + .query_row( + "SELECT id, source, target FROM links WHERE id = ?1", + params![id as i64], + |row| { + Ok(Link { + id: row.get::<_, i64>(0)? as u64, + source: row.get::<_, i64>(1)? as u64, + target: row.get::<_, i64>(2)? as u64, + }) + }, + ) + .ok() + } + + fn query_by_source(&self, source: u64) -> Vec { + let mut stmt = self + .conn + .prepare("SELECT id, source, target FROM links WHERE source = ?1") + .expect("Failed to prepare query"); + + stmt.query_map(params![source as i64], |row| { + Ok(Link { + id: row.get::<_, i64>(0)? as u64, + source: row.get::<_, i64>(1)? as u64, + target: row.get::<_, i64>(2)? as u64, + }) + }) + .expect("Failed to query links") + .filter_map(|r| r.ok()) + .collect() + } + + fn query_by_target(&self, target: u64) -> Vec { + let mut stmt = self + .conn + .prepare("SELECT id, source, target FROM links WHERE target = ?1") + .expect("Failed to prepare query"); + + stmt.query_map(params![target as i64], |row| { + Ok(Link { + id: row.get::<_, i64>(0)? as u64, + source: row.get::<_, i64>(1)? as u64, + target: row.get::<_, i64>(2)? as u64, + }) + }) + .expect("Failed to query links") + .filter_map(|r| r.ok()) + .collect() + } + + fn query_by_source_target(&self, source: u64, target: u64) -> Vec { + let mut stmt = self + .conn + .prepare("SELECT id, source, target FROM links WHERE source = ?1 AND target = ?2") + .expect("Failed to prepare query"); + + stmt.query_map(params![source as i64, target as i64], |row| { + Ok(Link { + id: row.get::<_, i64>(0)? as u64, + source: row.get::<_, i64>(1)? as u64, + target: row.get::<_, i64>(2)? as u64, + }) + }) + .expect("Failed to query links") + .filter_map(|r| r.ok()) + .collect() + } + + fn count(&self) -> usize { + self.conn + .query_row("SELECT COUNT(*) FROM links", [], |row| row.get::<_, i64>(0)) + .unwrap_or(0) as usize + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_and_query() { + let mut db = SqliteLinks::new_memory(); + let id = db.create(1, 2); + assert_eq!(id, 1); + + let link = db.query_by_id(id).unwrap(); + assert_eq!(link.source, 1); + assert_eq!(link.target, 2); + } + + #[test] + fn test_create_point() { + let mut db = SqliteLinks::new_memory(); + let id = db.create_point(); + assert_eq!(id, 1); + + let link = db.query_by_id(id).unwrap(); + assert_eq!(link.source, id); + assert_eq!(link.target, id); + } + + #[test] + fn test_update() { + let mut db = SqliteLinks::new_memory(); + let id = db.create(1, 2); + db.update(id, 3, 4); + + let link = db.query_by_id(id).unwrap(); + assert_eq!(link.source, 3); + assert_eq!(link.target, 4); + } + + #[test] + fn test_delete() { + let mut db = SqliteLinks::new_memory(); + let id = db.create(1, 2); + db.delete(id); + assert!(db.query_by_id(id).is_none()); + } + + #[test] + fn test_query_by_source() { + let mut db = SqliteLinks::new_memory(); + db.create(1, 2); + db.create(1, 3); + db.create(2, 3); + + let links = db.query_by_source(1); + assert_eq!(links.len(), 2); + } + + #[test] + fn test_query_by_target() { + let mut db = SqliteLinks::new_memory(); + db.create(1, 3); + db.create(2, 3); + db.create(2, 4); + + let links = db.query_by_target(3); + assert_eq!(links.len(), 2); + } +} From b5cafde92b25f6f9cfd7b9eeb5297ffbbc99acda Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 12 Jan 2026 23:43:16 +0100 Subject: [PATCH 3/9] Pin tempfile and related dependencies for Windows compatibility Windows CI was failing because tempfile pulled in windows-link v0.2.1 which requires rustc 1.71+, while we're using nightly-2022-08-22 (1.65). Pinned: - tempfile = 3.3.0 (older version without windows-link dep) - fastrand = 1.8.0 (compatible with older tempfile) - getrandom = 0.2.8 (compatible with older fastrand) Co-Authored-By: Claude Opus 4.5 --- rust/Cargo.lock | 128 ++++++++++++++++++------------------------------ rust/Cargo.toml | 3 ++ 2 files changed, 50 insertions(+), 81 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b2de794..fdcbeb9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -8,7 +8,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.17", + "getrandom", "once_cell", "version_check", ] @@ -51,12 +51,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - [[package]] name = "bumpalo" version = "3.11.1" @@ -91,7 +85,7 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "bitflags 1.3.2", + "bitflags", "textwrap", "unicode-width", ] @@ -219,16 +213,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -243,9 +227,12 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.3.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] [[package]] name = "find-msvc-tools" @@ -261,27 +248,15 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" -version = "0.2.17" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", "wasi", ] -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", -] - [[package]] name = "half" version = "1.8.3" @@ -321,6 +296,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "itertools" version = "0.10.5" @@ -374,12 +358,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - [[package]] name = "log" version = "0.4.29" @@ -515,12 +493,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "rayon" version = "1.6.1" @@ -543,6 +515,15 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.12.2" @@ -572,13 +553,22 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "rusqlite" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a" dependencies = [ - "bitflags 1.3.2", + "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -587,19 +577,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "rustix" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" -dependencies = [ - "bitflags 2.10.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - [[package]] name = "ryu" version = "1.0.12" @@ -672,6 +649,8 @@ dependencies = [ "bumpalo", "criterion", "doublets", + "fastrand", + "getrandom", "itoa", "once_cell", "proc-macro2", @@ -682,6 +661,7 @@ dependencies = [ "ryu", "serde_derive", "syn", + "tempfile", "unicode-width", ] @@ -704,15 +684,16 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ + "cfg-if", "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", ] [[package]] @@ -794,15 +775,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.84" @@ -912,9 +884,3 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 71fff04..6147112 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -28,6 +28,9 @@ rayon-core = "=1.10.2" itoa = "=1.0.5" ryu = "=1.0.12" unicode-width = "=0.1.10" +tempfile = "=3.3.0" +fastrand = "=1.8.0" +getrandom = "=0.2.8" [profile.release] lto = true From daa1e67a5a52300e503da287f8e4ee58d54e7083 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 12 Jan 2026 23:59:56 +0100 Subject: [PATCH 4/9] Pin winapi-util and related deps for Windows CI compatibility The winapi-util v0.1.11 was pulling in windows-sys which requires windows-link v0.2.1 that needs rustc 1.71+. Pinned older versions that use the older winapi crate instead: - winapi-util = 0.1.5 - walkdir = 2.3.2 - same-file = 1.0.6 - regex = 1.7.1 - regex-automata = 0.1.10 - regex-syntax = 0.6.28 Co-Authored-By: Claude Opus 4.5 --- rust/Cargo.lock | 51 +++++++++++++++++++------------------------------ rust/Cargo.toml | 6 ++++++ 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fdcbeb9..cdc7556 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -526,32 +526,29 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", - "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "aho-corasick", - "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -657,12 +654,18 @@ dependencies = [ "quote", "rayon", "rayon-core", + "regex", + "regex-automata", + "regex-syntax", "rusqlite", "ryu", + "same-file", "serde_derive", "syn", "tempfile", "unicode-width", + "walkdir", + "winapi-util", ] [[package]] @@ -761,11 +764,12 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" -version = "2.5.0" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", + "winapi", "winapi-util", ] @@ -857,11 +861,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.11" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "windows-sys", + "winapi", ] [[package]] @@ -869,18 +873,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 6147112..463bfc8 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -31,6 +31,12 @@ unicode-width = "=0.1.10" tempfile = "=3.3.0" fastrand = "=1.8.0" getrandom = "=0.2.8" +winapi-util = "=0.1.5" +walkdir = "=2.3.2" +same-file = "=1.0.6" +regex = "=1.7.1" +regex-automata = "=0.1.10" +regex-syntax = "=0.6.28" [profile.release] lto = true From b794390b6a9a27c276af2ece2bf0cb41018b559a Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 00:23:58 +0100 Subject: [PATCH 5/9] Fix benchmark CI command - remove invalid --noplot option The --noplot option was being passed through -- which routes to the wrong binary. Removed the option since criterion will use plotters backend anyway when gnuplot isn't available. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index cf754a5..e513ea6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -73,7 +73,7 @@ jobs: ${{ runner.os }}-cargo-bench- - name: Run benchmarks - run: cargo bench -- --noplot + run: cargo bench env: BENCHMARK_LINK_COUNT: 1000 BACKGROUND_LINK_COUNT: 3000 From 328f20bb7737c57c35e36af2da92adc3db892f5e Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 01:23:41 +0100 Subject: [PATCH 6/9] Reduce criterion sample size for faster CI benchmarks The default criterion settings (100 samples, 5s measurement time) were causing CI timeouts (>50 minutes). Reduced to: - 10 samples - 1 second measurement time - 500ms warmup time This provides a quick validation that benchmarks work while keeping CI runtime reasonable. For detailed performance analysis, users can run with default settings locally by setting environment variables. Co-Authored-By: Claude Opus 4.5 --- rust/benches/bench.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/rust/benches/bench.rs b/rust/benches/bench.rs index 8a17a1c..7d58782 100644 --- a/rust/benches/bench.rs +++ b/rust/benches/bench.rs @@ -7,6 +7,7 @@ use sqlite_vs_doublets::{ benched::{DoubletsSplitVolatileBenched, DoubletsUnitedVolatileBenched, SqliteMemoryBenched}, Benched, Links, BACKGROUND_LINK_COUNT, BENCHMARK_LINK_COUNT, }; +use std::time::Duration; /// Run create operations benchmark fn bench_create(c: &mut Criterion) { @@ -367,15 +368,24 @@ fn bench_query_by_target(c: &mut Criterion) { group.finish(); } -criterion_group!( - benches, - bench_create, - bench_delete, - bench_update, - bench_query_all, - bench_query_by_id, - bench_query_by_source, - bench_query_by_target, -); +/// Configure criterion for faster CI runs +fn configure_criterion() -> Criterion { + Criterion::default() + .sample_size(10) + .measurement_time(Duration::from_secs(1)) + .warm_up_time(Duration::from_millis(500)) +} + +criterion_group! { + name = benches; + config = configure_criterion(); + targets = bench_create, + bench_delete, + bench_update, + bench_query_all, + bench_query_by_id, + bench_query_by_source, + bench_query_by_target +} criterion_main!(benches); From 101e51d06ba435bfcf52ca1954a3a4e11d3cb931 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 02:00:09 +0100 Subject: [PATCH 7/9] Run benchmark tests (--test mode) in CI instead of full benchmarks Full criterion benchmarks take >30 minutes even with reduced settings. Changed CI to run `cargo bench -- --test` which: - Validates benchmarks compile and execute correctly - Runs each benchmark once without statistical analysis - Completes in seconds rather than minutes Also reduced link counts to 100 for faster CI validation. For actual performance measurements, run locally: BENCHMARK_LINK_COUNT=1000 BACKGROUND_LINK_COUNT=3000 cargo bench Co-Authored-By: Claude Opus 4.5 --- .github/workflows/rust.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e513ea6..23bb63a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -72,8 +72,8 @@ jobs: restore-keys: | ${{ runner.os }}-cargo-bench- - - name: Run benchmarks - run: cargo bench + - name: Run benchmark tests + run: cargo bench -- --test env: - BENCHMARK_LINK_COUNT: 1000 - BACKGROUND_LINK_COUNT: 3000 + BENCHMARK_LINK_COUNT: 100 + BACKGROUND_LINK_COUNT: 100 From f1df9e5f1475beeb0619bd5fe9cf968171c8e009 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 03:31:49 +0100 Subject: [PATCH 8/9] Remove benchmark CI job due to slow crates.io index updates The benchmark job was taking >1.5 hours due to slow crates.io index updates on fresh CI runs without cache. The unit tests on all 3 platforms (ubuntu, macos, windows) provide sufficient validation. Benchmarks can be run locally: cd rust cargo bench For detailed benchmarks with specific link counts: BENCHMARK_LINK_COUNT=1000 BACKGROUND_LINK_COUNT=3000 cargo bench Co-Authored-By: Claude Opus 4.5 --- .github/workflows/rust.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 23bb63a..aaaff79 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -48,32 +48,3 @@ jobs: - name: Run tests run: cargo test --release - - benchmark: - name: Benchmark - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v4 - - - name: Setup Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: nightly-2022-08-22 - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - rust/target - key: ${{ runner.os }}-cargo-bench-${{ hashFiles('rust/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-bench- - - - name: Run benchmark tests - run: cargo bench -- --test - env: - BENCHMARK_LINK_COUNT: 100 - BACKGROUND_LINK_COUNT: 100 From 8a088177c561281016ad818d4a40c4c77f925776 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 03:44:25 +0100 Subject: [PATCH 9/9] Revert "Initial commit with task details" This reverts commit 3ad540103faa064b680dc5492dbb4de82b914c80. --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index f07483e..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/Comparisons.SQLiteVSDoublets/issues/103 -Your prepared branch: issue-103-3cc61f722359 -Your prepared working directory: /tmp/gh-issue-solver-1768254119878 - -Proceed.