Benchmarks
Fast where it counts.
One recorded run, five engines, the same rows, the same operations and the same timer. The board at the top shows where XOD leads; the cards below show every configuration, including the ones where another engine is faster. Nothing on this page is typed by hand — it is rendered from the result files in the repository.
Where XOD leads
Fastest engine per metric across the measured configurations, across XOD’s three storage formats. The cards below compare the selected XOD format with SQLite and LiteDB.
Flat table · 250 MB · 100k rows
One table, one indexed column — the shape of a list screen with search and paging. These are test sizes, not a limit.
15 runs · 5 engines · 8 metrics
What this means
Loading a large table in one go: an import, a restore, a first sync. One transaction per engine, payload included, fsync settings disclosed where they differ.
What this means
The cost of starting up and writing one record: what launching a desktop tool feels like. Fresh process, existing database, one insert and commit. Includes index load.
What this means
Fetching a thousand known records by id, as a detail screen or a batch job would. Random primary keys, warm process, index lookup plus row read per call.
What this means
Twenty ordinary filters returning 500 rows each: the list screens of a real application. Indexed secondary key, 10,000 rows hydrated in total; dominates list screens.
What this means
Opening a page near the end of a big table without reading everything before it. Offset pagination. Keyset pagination would make SQLite and LiteDB competitive again.
What this means
A hundred records edited and a hundred removed, as a bulk action would. Read-modify-write per row, batched commit; XOD shares the batch.
What this means
How much space the database takes on disk after the load. All files in the database directory, including indexes and journal.
What this means
The most memory the process used while doing the work. Peak working set of an isolated worker process; includes the page cache.
Flat table · 1 GB · 400k rows
One table, one indexed column — the shape of a list screen with search and paging. These are test sizes, not a limit.
15 runs · 5 engines · 8 metrics
What this means
Loading a large table in one go: an import, a restore, a first sync. One transaction per engine, payload included, fsync settings disclosed where they differ.
What this means
The cost of starting up and writing one record: what launching a desktop tool feels like. Fresh process, existing database, one insert and commit. Includes index load.
What this means
Fetching a thousand known records by id, as a detail screen or a batch job would. Random primary keys, warm process, index lookup plus row read per call.
What this means
Twenty ordinary filters returning 500 rows each: the list screens of a real application. Indexed secondary key, 10,000 rows hydrated in total; dominates list screens.
What this means
Opening a page near the end of a big table without reading everything before it. Offset pagination. Keyset pagination would make SQLite and LiteDB competitive again.
What this means
A hundred records edited and a hundred removed, as a bulk action would. Read-modify-write per row, batched commit; XOD shares the batch.
What this means
How much space the database takes on disk after the load. All files in the database directory, including indexes and journal.
What this means
The most memory the process used while doing the work. Peak working set of an isolated worker process; includes the page cache.
Related records · 10k customers
Customers, orders, order lines and products — inserted, loaded and deleted as a graph.
15 runs · 5 engines · 3 metrics
What this means
Writing customers together with their orders and lines: related rows that must stay consistent. Normalized four-level graph; XOD inserts the object graph, SQLite uses one transaction.
What this means
Two hundred detail screens, each loading a customer with orders and lines. Each load is customer + 5 orders + 20 lines + products.
What this means
Deleting customers and everything that belongs to them, without leaving orphan rows. Deletes customer, orders and order lines; validated by row counts after the run.
Related records · 40k customers
Customers, orders, order lines and products — inserted, loaded and deleted as a graph.
15 runs · 5 engines · 3 metrics
What this means
Writing customers together with their orders and lines: related rows that must stay consistent. Normalized four-level graph; XOD inserts the object graph, SQLite uses one transaction.
What this means
Two hundred detail screens, each loading a customer with orders and lines. Each load is customer + 5 orders + 20 lines + products.
What this means
Deleting customers and everything that belongs to them, without leaving orphan rows. Deletes customer, orders and order lines; validated by row counts after the run.
Readers and a writer · 250 MB · 100k rows
Eight threads reading while one thread writes, for five seconds, on the 100k-row table.
15 runs · 5 engines · 4 metrics
What this means
A busy service: eight threads reading while one thread writes. 5 s window, eight reader threads plus one writer.
What this means
How much writing still gets through while eight threads read. A reader-heavy workload can starve the writer; this is the number that shows it.
What this means
Half of reads finish faster than this while eight threads are active. Median read latency under eight readers plus one writer.
What this means
One read in a hundred is slower than this under load: the tail users feel. 99th percentile read latency under eight readers plus one writer.
Method
What the benchmark guarantees.
- Isolated process per run. A fresh worker for every engine, suite, scale and iteration.
- Rotated order, repeated. Engines rotate each iteration; the page shows medians.
- Same questions. One runner, one timer, the same POCOs and the same operations for every engine.
- Answers validated. Counts, ids, graph shape and persisted values are checked, so a fast wrong answer fails the run.
- Durability disclosed. XOD and SQLite run without fsync; LiteDB’s writes include its own, noted where it matters.
- Page size disclosed. Every XOD database runs at 512 KB pages. Larger pages buy bulk-load time and cost mutations; smaller pages help relational reads on the document formats and roughly double bulk load. The trade-off is in the tuning notes.
What it does not prove
One machine, one storage volume, one set of shapes. Offset pagination flatters XOD and would look different with keyset paging. Reads on JSON and Binary pay for a page parse that XML does not. Treat the cards as questions to ask about your own workload, not as a verdict.
Where to check a number
The page builds from 1 committed result files. Each is named with the version and commit that produced it; the raw JSON is the record, this page is a rendering of it.