Skip to content

Repository files navigation

Comparisons.SpacetimeDBVSDoublets

Benchmark comparing SpacetimeDB 2 vs Doublets performance for basic CRUD operations with links.

SpacetimeDB is benchmarked using the official spacetimedb-sdk Rust crate connected to a running SpacetimeDB 2.0 server. Doublets is benchmarked with both in-memory (volatile) and file-backed (non-volatile) storage variants.

Benchmark Operations

Operation Description
Create Create a self-referential point link (id == source == target)
Delete Delete links by id
Update Update link source and target
Query All Retrieve all links ([*, *, *])
Query by Id Retrieve a link by id
Query by Source Retrieve all links with a given source
Query by Target Retrieve all links with a given target

Backends Benchmarked

SpacetimeDB

  • SpacetimeDB — connects to a running SpacetimeDB 2.0 server via the official spacetimedb-sdk Rust crate; uses the links table defined in the spacetime-module WebAssembly module

The benchmark uses the official SpacetimeDB Rust client SDK, calling reducers to mutate data and reading from the client-side subscription cache.

Doublets

  • Doublets United Volatile — in-memory store; links stored as contiguous (index, source, target) units
  • Doublets Split Volatile — in-memory store; separate data and index memory regions
  • Doublets United NonVolatile — file-backed store; same contiguous layout but memory-mapped to a single file; data persists to disk
  • Doublets Split NonVolatile — file-backed store; separate data and index files; both memory-mapped; data persists to disk

Doublets uses a recursive-less size-balanced tree for O(1) lookup by id and O(log n + k) traversal by source/target. The file-backed variants use memmap2 for memory-mapped file I/O, flushing changes to disk on drop via sync_all(). See rust/doublets-patched/PATCHES.md for why a local patched copy is used instead of the published crates.io version.

Benchmark Background

Each benchmark iteration pre-populates the database with background links to simulate a realistic database state:

  • Background links: BACKGROUND_LINK_COUNT (default: 3000) — already present before measurement
  • Benchmark links: BENCHMARK_LINK_COUNT (default: 1000) — the operations being measured

Results

The numbers below represent the amount of time (ns) a single benchmark iteration takes.

  • The first chart shows time in a pixel (linear) scale. Doublets bars are drawn with a minimum visible width, otherwise they would not be visible next to SpacetimeDB.
  • The second chart shows time in a logarithmic scale, to see the difference clearly, because it is around 3-5 orders of magnitude.

Charts and the table are recalculated by the Rust Benchmark workflow on every push to main and committed back to this repository, so the results are visible here without running the benchmark locally.

Rust

Image of Rust benchmark (pixel scale) Image of Rust benchmark (log scale)

Raw benchmark results (all numbers are in nanoseconds)

Generated 2026-09-16 09:20 UTC by GitHub Actions run 35075414684 — 1000 benchmarked links, 3000 background links.

Operation Doublets United Volatile Doublets United NonVolatile Doublets Split Volatile Doublets Split NonVolatile SpacetimeDB
Create 76689 (33385.0x faster) 76567 (33438.2x faster) 47102 (54355.7x faster) 47395 (54019.7x faster) 2560262853
Update 247101 (10703.3x faster) 247021 (10706.8x faster) 35819 (73838.1x faster) 36029 (73407.7x faster) 2644805261
Delete 180271 (7169.4x faster) 181398 (7124.8x faster) 94116 (13732.3x faster) 100143 (12905.9x faster) 1292432593
Query All 21580 (1.0x faster) 21592 (1.0x faster) 28388 (1.3x slower) 28312 (1.3x slower) 22126
Query by Id 55 (198863.3x faster) 59 (185381.0x faster) 1018 (10744.1x faster) 1018 (10744.1x faster) 10937480
Query by Source 1476 (110.8x faster) 1453 (112.5x faster) 480 (340.7x faster) 478 (342.1x faster) 163528
Query by Target 1514 (109.3x faster) 1478 (111.9x faster) 445 (371.8x faster) 393 (420.9x faster) 165432

Each Doublets cell is annotated with how many times faster (or slower) it is than SpacetimeDB for the same operation.

Conclusion

Doublets is an embedded store: an operation is a few pointer dereferences and tree rotations in memory (or in a memory-mapped file), while every SpacetimeDB operation is a reducer call over a WebSocket connection to a separate process, and every query is served from the client-side subscription cache. The measured difference is dominated by that architectural difference rather than by the data structures themselves.

To get fresh numbers, please fork the repository and rerun the benchmark in GitHub Actions.

Operation Complexity

Operation SpacetimeDB Doublets United Doublets Split
Create O(log n) + network O(log n) O(log n)
Delete O(log n) + network O(log n) O(log n)
Update O(log n) + network O(log n) O(log n)
Query All O(n) cache read O(n) O(n)
Query by Id O(n) cache scan O(1) O(1)
Query by Source O(n) cache scan O(log n + k) O(log n + k)
Query by Target O(n) cache scan O(log n + k) O(log n + k)

The algorithmic complexity is the same for volatile and non-volatile Doublets variants. The non-volatile variants have additional I/O overhead due to memory-mapped file writes (flushed to disk on drop).

Related Benchmarks

Running Benchmarks

Prerequisites

  • Rust nightly, pinned in rust/rust-toolchain.toml (rustup installs it automatically)
  • SpacetimeDB CLI: curl -sSf https://install.spacetimedb.com | sh

Start SpacetimeDB server and publish module

# Start the local SpacetimeDB server
spacetime start &

# Build and publish the links module
spacetime build --project-path rust/spacetime-module
spacetime publish --project-path rust/spacetime-module --yes benchmark-links

Run benchmarks

cd rust

# Full benchmark run (1000 links, 3000 background)
SPACETIMEDB_URI=http://localhost:3000 SPACETIMEDB_DB=benchmark-links \
  cargo bench --bench bench -- --output-format bencher | tee out.txt

# Quick benchmark run (CI scale)
BENCHMARK_LINK_COUNT=10 BACKGROUND_LINK_COUNT=100 \
SPACETIMEDB_URI=http://localhost:3000 SPACETIMEDB_DB=benchmark-links \
  cargo bench --bench bench

# Generate the results table and charts from out.txt
python3 out.py out.txt --results results.md

# Regenerate everything the CI publishes: results.md, docs/benchmarks/ charts
# and the results section of README.md
python3 out.py out.txt --results results.md --readme ../README.md \
  --docs-dir ../docs/benchmarks

Run tests

cd rust
SPACETIMEDB_URI=http://localhost:3000 SPACETIMEDB_DB=benchmark-links cargo test

Code quality

cd rust
cargo fmt --all
cargo clippy --all-targets

# Unit tests for the results reporting pipeline (no benchmark run required)
python3 -m unittest test_out -v

Project Structure

.
├── docs/
│   └── benchmarks/             # Benchmark charts published by CI and shown above
│       ├── bench_rust.png
│       └── bench_rust_log_scale.png
├── rust/
│   ├── spacetime-module/       # SpacetimeDB WASM module (links table + reducers)
│   │   ├── Cargo.toml
│   │   └── src/
│   │       └── lib.rs          # Table definition and reducers using `spacetimedb` crate
│   ├── Cargo.toml              # Package manifest with pinned dependencies
│   ├── doublets-patched/       # Local patches to doublets-rs for modern nightly compatibility
│   │   └── PATCHES.md          # Documents why patches are needed and what was changed
│   ├── rust-toolchain.toml     # Pinned Rust nightly toolchain
│   ├── rustfmt.toml            # Rust formatting config
│   ├── out.py                  # Results table, charts and README update
│   ├── test_out.py             # Unit tests for out.py
│   ├── results.md              # Generated results table (committed by CI)
│   ├── src/
│   │   ├── lib.rs              # Links trait, constants (BENCHMARK_LINK_COUNT, BACKGROUND_LINK_COUNT)
│   │   ├── module_bindings/    # spacetimedb-sdk client bindings for the links module
│   │   ├── spacetimedb_impl.rs # SpacetimeDB SDK client (implements Links)
│   │   ├── doublets_impl.rs    # Doublets store adapters (implements Links)
│   │   ├── exclusive.rs        # Exclusive<T> wrapper for interior mutability
│   │   ├── fork.rs             # Fork<B> — benchmark iteration isolation
│   │   └── benched/
│   │       ├── mod.rs          # Benched trait (setup/fork/unfork lifecycle)
│   │       ├── spacetimedb_benched.rs  # Benched impl for SpacetimeDB
│   │       └── doublets_benched.rs     # Benched impls for Doublets stores
│   └── benches/
│       └── bench.rs            # Criterion benchmark suite (7 operations x 5 backends)
└── .github/
    └── workflows/
        └── rust-benchmark.yml  # CI: test on Linux/macOS, benchmark, publish results

License

Unlicense — Public Domain

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages