Performance

SNKV vs SQLite
on KV Workloads

Both engines use the same B-tree storage core, WAL journal, and page cache. The gap is the SQL layer — even with prepared statements, every call still runs through the VDBE interpreter and SQLite's record format, which SNKV skips by calling the B-tree directly.

1M records · Linux x86-64 · 5 runs averaged · WAL · SYNC_NORMAL
2.32×
faster random reads
588K vs 254K ops/s
2.49×
faster exists checks
653K vs 262K ops/s
1.25×
faster mixed workload
317K vs 254K ops/s
1.22×
faster sequential scan
13.5M vs 11.1M ops/s

Operation-by-Operation

SQLite uses a WITHOUT ROWID table with a BLOB primary key — the fairest possible comparison. Both share identical page cache, WAL, and sync settings, the same SQLite 3.51.2 core, and are compiled with gcc -O2. All differences come from the layer above the B-tree.

SNKV SQLite (WITHOUT ROWID)
Sequential Writes
SNKV
1.51M ops/s
1.16×
SQLite
1.30M ops/s
Random Reads
SNKV
588K ops/s
2.32×
SQLite
254K ops/s
Sequential Scan
SNKV
13.5M ops/s
1.22×
SQLite
11.1M ops/s
Exists Checks
SNKV
653K ops/s
2.49×
SQLite
262K ops/s
Mixed Workload
70% read / 20% write / 10% delete
SNKV
317K ops/s
1.25×
SQLite
254K ops/s
Random Updates
SNKV
120K ops/s
1.11×
SQLite
108K ops/s
Random Deletes
SNKV
126K ops/s
1.01×
SQLite
124K ops/s
Bulk Insert
1M rows in one transaction
SNKV
1.75M ops/s
1.17×
SQLite
1.50M ops/s

Methodology

Analysis

Where the Difference Comes From

SNKV and SQLite use the same B-tree storage engine at the bottom. Every performance difference comes from what each layer adds on top of it.

Parsing & planning: done once
The SQLite benchmark uses prepared statements, so SQL is parsed and planned once, not per operation. The remaining per-call cost is binding, stepping and resetting the statement, plus encoding and decoding SQLite's record format — SNKV skips all of that and calls the B-tree API directly.
Reads: about 2.3–2.5×
Point reads and exists checks show the largest gap. The B-tree work (one seek) is small, so SQLite's per-statement overhead is a large share of each call — and SNKV's cached read cursor avoids opening a cursor per lookup.
No VDBE interpreter
SQLite compiles queries to bytecode run by a virtual machine. SNKV calls compiled C functions. No bytecode dispatch loop on the critical path.
Cached read cursor
SNKV keeps a persistent read cursor open per column family. SQLite opens and closes a new cursor for each statement. Cursor open/close touches the pager — SNKV skips it on every read.
Writes: 1.0–1.2×
For inserts, updates and deletes the B-tree write itself (page changes, WAL frames, rebalancing) is most of the cost and is identical in both engines, so the layer above matters less.
WAL auto-checkpoint: same rule as SQLite
Both engines checkpoint the WAL once it reaches 1000 pages, so neither pays for an oversized WAL. Before SNKV did this (it opens the B-tree directly, so SQLite's built-in hook was never installed), its WAL grew to the size of the database and every page-cache miss had to search a large WAL index — that alone made SNKV 3–10% slower than SQLite on the mixed workload and cost 8–16% on reads.

Source

Run It Yourself

Both benchmarks are open source. Clone and build to reproduce results on your hardware.

C benchmark — SNKV

# From the SNKV repo root (optimised build, -O2)
make BUILD=release tests/test_benchmark
./tests/test_benchmark

C benchmark — SQLite comparison

# https://github.com/hash-anu/sqllite-benchmark-kv
git clone https://github.com/hash-anu/sqllite-benchmark-kv
cd sqllite-benchmark-kv
make sqlite_benchmark      # SQLite 3.51.2, -O2
./sqlite_benchmark

Additional comparisons against LMDB and RocksDB:
LMDB — github.com/hash-anu/lmdb-benchmark
RocksDB — github.com/hash-anu/rocksdb-benchmark