Benchmark
Five passes against a server that is already running. Each pass rebuilds the database benchmark. The page shows the mean time.
Run it
Start the listener first. Security stays off, which is the default. Then:
make benchmark
The port defaults to 27018. BENCH_ROWS defaults to 1000 orders. BENCH_LOOKUPS defaults to 100 point reads. The script then asks whether to repeat the same steps on a MongoDB already listening on port 27017. Answering no runs Kova only. Answering yes drops and recreates only the database named benchmark on that server. BENCH_MONGO=1 answers yes. MONGO_PORT chooses the MongoDB port.
make benchmark KOVA_PORT=27019 BENCH_ROWS=1000 BENCH_LOOKUPS=100
The run creates the database benchmark. It does not need that database to exist already. If a previous run left one, that database is removed first. Other databases stay. A release binary is the number worth keeping. Stop the listener that is already up, then start that binary with make run-release. make release only builds it. A debug binary still runs, and the page names which binary answered.
What it measures
Each order is sku, dept, job, sal, qty, and note. job cycles through clerk, analyst, manager, and salesman. note contains that word, so a text search for clerk hits a quarter of the rows. bonus is set on every 10th order. Four departments sit in dept. After the indexes exist, sku 0 is deleted so the point lookups start at 1.
MQL inserts the rows, builds a unique index, a range index, a hash index, a compound index, and a sparse index, then updates, deletes, numbers a field, commits one transaction, and aborts another. It reads by index, scans an unindexed field, groups, looks up dept, writes high_pay with $out, and searches the text view orders_search, including a misspelling.
SQL reads those same rows: a point lookup, a range, a count, a group, an inner join, text search, a fuzzy match, and groups. CREATE TABLE, INSERT, UPDATE, and DELETE are sent as SQL and written to the pages. One update and one delete change a single row. Another update and another delete match every order whose qty is 7.
The second point-lookup pass runs after the first pass has already read the pages. Index, scan, hit, miss, and WAL are the change in serverStatus during that step. Hit, miss, and WAL are Kova only.
How the times are compared
The benchmark runs the whole pass five times. BENCH_REPEATS changes that number. Each pass drops and recreates the database benchmark. The time in the table is the mean of those passes. Rows, index, scan, hit, miss, and WAL are means too. The smaller line under a time is the sample standard deviation: the square root of the sum of squared distances from the mean, after that sum is divided by one less than the number of passes.
When MongoDB ran the same step, each difference is Kova minus Mongo on that pass. If the absolute value of the mean difference is less than or equal to the standard deviation of those differences, the step is similar. A similar step is not a win for either side, and the row is not tinted. A tinted row means Kova's mean was faster by more than that standard deviation. The line under the MQL table counts Kova's wins, MongoDB's wins, the similar steps, and the steps that could not be compared because MongoDB does not have that feature. Those last steps are listed at the end of the MQL table. The SQL table lists Kova only.
| Column | What the number is |
|---|---|
| Operation | The name of the step. The button opens the command that ran. |
| Result | ok means the server finished the step. fail means it returned an error. |
| Rows | Documents inserted, changed, deleted, or returned. |
| Time | Mean wall-clock time of the passes, from send to reply. The smaller line is the sample standard deviation. When MongoDB ran, the MQL cell names Kova and Mongo. The last line is Kova minus Mongo, then Kova's mean divided by Mongo's, or the word similar when that difference stays within the standard deviation of the paired differences. Neither side wins a similar step. An oxide mark and a tinted row mean Kova was faster by more than that. The count under the MQL table is Kova's wins, MongoDB's wins, the similar steps, and the steps MongoDB does not have. The SQL table lists Kova only. |
| Rows/s | Rows divided by the time. A step with no rows has no rate. |
| Index | Kova is indexLookups: how many times the step chose an index. One range query is 1, however many documents come back. Mongo is keys examined: how many index keys the step walked. A range over 675 keys is 675. The two numbers count different events. N/A means not available. |
| Scan | How many times the step started a collection scan, on Kova and on Mongo. One find that walks the collection is 1. The pages that walk reads are under Hit. |
| Hit | Kova only. Buffer-pool pages served from memory during the step. Reading a collection once counts one hit per page, so Scan can be 1 while Hit is 77. |
| Miss | Kova only. Buffer-pool pages read from disk because they were not cached. |
| WAL | Kova only. Log records appended by the step. Many documents can share one record. |
| Note | Extra detail for the step. |
This run
| Figure | Value |
|---|---|
| When | 28 Oct 2026, 09:49:47 CET |
| Address | 127.0.0.1:27018 |
| MongoDB | 8.0.19 at 127.0.0.1:27017 |
| Process | /home/marcelo/Documents/git/kova/target/release/kova (release) |
| Workload | 1,000 orders, 100 point lookups. The run creates the database benchmark. |
| Operating system | Ubuntu 24.04.4 LTS, Linux 6.8.0-139-generic x86_64 |
| RAM | 31.2 GiB |
| Disk | 286.3 GiB free of 915.3 GiB |
| Video memory | NVIDIA GeForce RTX 3060, 12.0 GiB |
The inserted documents are benchmark-data.json. The timings and the process figures are benchmark-result.json.
Process memory and disk
| Figure | This process |
|---|---|
| Memory in use at the end | 373.9 MiB |
| Memory growth during the run | 115.5 MiB |
| Memory high-water | 373.9 MiB |
| Bytes read | 0 B |
| Bytes written | 3.8 MiB |
Times
This run repeated each step 5 times. Each pass drops and recreates the database benchmark. The time is the mean of those passes. The smaller line is the sample standard deviation: the square root of the sum of squared distances from the mean, after that sum is divided by one less than the number of passes. A step is similar when MongoDB also finished it and the absolute difference of the two means is less than or equal to the standard deviation of the paired differences, Kova minus Mongo on the same pass. A similar step is not a win. A tinted row is a step Kova won by more than that.
Each paired cell names Kova and Mongo. The last line is Kova minus Mongo in milliseconds, then how many times Kova's mean is Mongo's. An oxide mark sits beside the faster time. The whole row is tinted when Kova was faster by more than the standard deviation. The last line says similar when the difference of the means stayed within the standard deviation of the paired differences, and neither side wins. Under the MQL table, the count is how many steps Kova won, how many MongoDB won, how many were similar, and how many could not be compared because MongoDB does not have that feature. Those steps are listed last. The SQL table lists Kova only. N/A means not available: MongoDB did not run that step, or did not report that counter. Index on Kova is how many times the step chose an index. One range is 1. Index on Mongo is how many index keys the step walked. Scan is how many collection scans the step started, and one walk of the collection is 1. Hit is Kova only: pages read from the buffer pool. A scan of 1 can show many hits, one per page.
MQL
| Operation | Result | Rows | Time | Rows/s | Index | Scan | Hit | Miss | WAL | Note |
|---|---|---|---|---|---|---|---|---|---|---|
| ok | 1 | Kova44.8 ms± 9 msMongo71.6 ms± 23.2 ms-27 ms, 0.6× | 22 | Kova0Mongo0 | Kova0Mongo0 | 22 | 0 | 4 | Removed a previous benchmark database. | |
| ok | 3 | Kova17.2 ms± 0.4 msMongo100.2 ms± 2.5 ms-83 ms, 0.2× | 174 | Kova0Mongo0 | Kova0Mongo0 | 0 | 0 | 3 | Creates dept, orders, and ledger. Nothing is assumed to exist. | |
| ok | 4 | Kova12.2 ms± 1.1 msMongo16.4 ms± 3.7 ms-4 ms, 0.7× | 328 | Kova0Mongo0 | Kova0Mongo0 | 0 | 0 | 1 | ||
| ok | 1,000 | Kova108.4 ms± 6.7 msMongo71.4 ms± 5.3 ms+37 ms, 1.5× | 9,225 | Kova0Mongo0 | Kova0Mongo0 | 1 | 0 | 2 | ||
| ok | 1,000 | Kova39.4 ms± 2.2 msMongo104.2 ms± 2.2 ms-65 ms, 0.4× | 25,381 | Kova0Mongo0 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 1,000 | Kova41.2 ms± 2.3 msMongo99 ms± 0 ms-58 ms, 0.4× | 24,272 | Kova0Mongo0 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 1,000 | Kova354.6 ms± 696.2 msMongo101.4 ms± 3.2 mssimilar | 2,820 | Kova0Mongo0 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 1,000 | Kova46.4 ms± 4.4 msMongo100 ms± 0.7 ms-54 ms, 0.5× | 21,552 | Kova0Mongo0 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 1,000 | Kova44.6 ms± 6.6 msMongo96.6 ms± 2.3 ms-52 ms, 0.5× | 22,422 | Kova0Mongo0 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 1,000 | Kova92 ms± 6.3 msMongo82 ms± 13.7 mssimilar | 10,870 | Kova0Mongo0 | Kova0Mongo1 | 16 | 0 | 1 | ||
| ok | 1 | Kova13.8 ms± 0.8 msMongo8 ms± 1.4 ms+5 ms, 1.7× | 72 | Kova0Mongo1 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 1 | Kova12.4 ms± 0.5 msMongo4.4 ms± 0.9 ms+8 ms, 2.8× | 81 | Kova0Mongo1 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 1 | Kova14.8 ms± 0.4 msMongo12.6 ms± 1.5 ms+2 ms, 1.2× | 68 | Kova0Mongo1 | Kova0Mongo0 | 16 | 0 | 1 | ||
| ok | 999 | Kova5.2 ms± 0.4 msMongo2.4 ms± 0.9 ms+3 ms, 2.2× | 192,115 | Kova0Mongo0 | Kova1Mongo0 | 15 | 0 | 0 | ||
| ok | 50 | Kova9.4 ms± 0.5 msMongo38.6 ms± 2.9 ms-29 ms, 0.2× | 5,319 | Kova0Mongo0 | Kova0Mongo0 | 0 | 0 | 1 | ||
| ok | 1 | Kova6.6 ms± 0.5 msMongo36.2 ms± 1.9 ms-30 ms, 0.2× | 152 | Kova0Mongo0 | Kova0Mongo0 | 0 | 0 | 1 | ||
| ok | 100 | Kova313.8 ms± 17.6 msMongo185.2 ms± 11.1 ms+128 ms, 1.7× | 319 | Kova100Mongo100 | Kova0Mongo0 | 100 | 0 | 0 | 100 findOne by sku | |
| ok | 100 | Kova289.2 ms± 24.7 msMongo134.2 ms± 11.8 ms+155 ms, 2.2× | 346 | Kova100Mongo100 | Kova0Mongo0 | 100 | 0 | 0 | 100 findOne by sku | |
| ok | 675 | Kova20.8 ms± 2.3 msMongo8.6 ms± 1.1 ms+12 ms, 2.4× | 32,452 | Kova1Mongo675 | Kova0Mongo0 | 14 | 0 | 0 | ||
| ok | 20 | Kova6 ms± 0.7 msMongo2.2 ms± 0.4 ms+4 ms, 2.7× | 3,333 | Kova0Mongo0 | Kova1Mongo1 | 15 | 0 | 0 | qty has no index | |
| ok | 4 | Kova12 ms± 0.7 msMongo4.8 ms± 0.4 ms+8 ms, 2.5× | 333 | Kova0Mongo0 | Kova1Mongo1 | 15 | 0 | 0 | ||
| ok | 100 | Kova8.2 ms± 0.8 msMongo4.8 ms± 0.8 ms+4 ms, 1.7× | 12,195 | Kova2Mongo100 | Kova1Mongo1 | 3 | 0 | 0 | ||
| ok | 675 | Kova51.4 ms± 3.4 msMongo50.2 ms± 3 mssimilar | 13,132 | Kova1Mongo675 | Kova1Mongo0 | 17 | 0 | 2 | ||
| ok | 8 | Kova3 ms± 0 msMongo3.2 ms± 0.4 mssimilar | 2,667 | Kova0Mongo0 | Kova0Mongo0 | 0 | 0 | 0 | ||
| ok | 1 | Kova6.2 ms± 0.4 msMongoN/A | 161 | Kova0MongoN/A | Kova0MongoN/A | 0 | 0 | 1 | ||
| ok | 2 | Kova10.8 ms± 0.4 msMongoN/A | 185 | Kova0MongoN/A | Kova0MongoN/A | 1 | 0 | 1 | ||
| ok | 1 | Kova5.4 ms± 0.5 msMongoN/A | 185 | Kova0MongoN/A | Kova0MongoN/A | 1 | 0 | 0 | ||
| ok | 1,000 | Kova163.6 ms± 3.3 msMongoN/A | 6,112 | Kova0MongoN/A | Kova0MongoN/A | 15 | 0 | 1 | ||
| ok | 250 | Kova13.8 ms± 2.3 msMongoN/A | 18,116 | Kova1MongoN/A | Kova0MongoN/A | 15 | 0 | 0 | ||
| ok | 100 | Kova86.2 ms± 6.1 msMongoN/A | 1,160 | Kova0MongoN/A | Kova0MongoN/A | 100 | 0 | 0 | ||
| ok | 100 | Kova72.8 ms± 1.3 msMongoN/A | 1,374 | Kova0MongoN/A | Kova0MongoN/A | 100 | 0 | 0 |
Kova was faster in 9 steps. MongoDB was faster in 11 steps. 4 steps were similar: the difference stayed within the standard deviation. 7 steps could not be compared, because MongoDB does not have that feature.
SQL
| Operation | Result | Rows | Time | Rows/s | Index | Scan | Hit | Miss | WAL | Note |
|---|---|---|---|---|---|---|---|---|---|---|
| ok | 1 | 3.6 ms± 0.5 ms | 278 | 0 | 0 | 0 | 0 | 0 | ||
| ok | 100 | 525.8 ms± 12.7 ms | 190 | 100 | 0 | 100 | 0 | 0 | 100 statements, WHERE sku = n | |
| ok | 100 | 536.8 ms± 21.5 ms | 186 | 100 | 0 | 100 | 0 | 0 | 100 statements, WHERE sku = n | |
| ok | 675 | 24.8 ms± 0.8 ms | 27,218 | 1 | 0 | 14 | 0 | 0 | ||
| ok | 4 | 22.6 ms± 4.2 ms | 177 | 0 | 1 | 15 | 0 | 0 | ||
| ok | 1 | 19 ms± 3.7 ms | 53 | 0 | 1 | 15 | 0 | 0 | ||
| ok | 999 | 23.8 ms± 1.6 ms | 41,975 | 0 | 2 | 16 | 0 | 0 | ||
| ok | 100 | 96.4 ms± 9.2 ms | 1,037 | 0 | 0 | 100 | 0 | 0 | ||
| ok | 100 | 74.2 ms± 4.1 ms | 1,348 | 0 | 0 | 100 | 0 | 0 | ||
| ok | 5 | 81 ms± 3.4 ms | 62 | 0 | 0 | 100 | 0 | 0 | ||
| ok | 7 | 1.6 ms± 0.5 ms | 4,375 | 0 | 0 | 0 | 0 | 0 | ||
| ok | 7 | 1.6 ms± 0.5 ms | 4,375 | 0 | 0 | 0 | 0 | 0 | ||
| ok | 675 | 16.6 ms± 2.9 ms | 40,663 | 1 | 0 | 14 | 0 | 0 | ||
| ok | 675 | 9.8 ms± 0.8 ms | 68,878 | 0 | 1 | 3 | 0 | 0 | ||
| ok | 0 | 6.2 ms± 0.4 ms | 0 | 0 | 0 | 0 | 0 | 1 | ||
| ok | 1 | 5.8 ms± 0.4 ms | 172 | 0 | 0 | 0 | 0 | 1 | ||
| ok | 1 | 106.2 ms± 3.2 ms | 9 | 0 | 0 | 30 | 0 | 1 | ||
| ok | 20 | 126.6 ms± 5.3 ms | 158 | 0 | 0 | 30 | 0 | 1 | ||
| ok | 1 | 6.2 ms± 0.4 ms | 161 | 0 | 0 | 2 | 0 | 1 | ||
| ok | 20 | 58.4 ms± 2.5 ms | 342 | 0 | 0 | 30 | 0 | 1 |
Resident memory of this Kova process when the run finished. The rest of the machine is not included.
Resident memory at the end minus resident memory at the start, for this process only.
The highest resident memory this process has reached, including what it held before the run.
Bytes this process read during the run.
Bytes this process wrote during the run.
The name opens the full command. A repeated lookup shows the statement and the range of keys.
The clock time of the run, in Central European Time.
The listener this run talked to.
The Kova binary that answered, and whether it was a debug or release build.
How many orders were inserted, and how many point lookups each pass ran.
The operating system this benchmark ran on.
Installed RAM on this machine.
Free and total space on the system disk.
Memory on the graphics card.
ok means the server finished the step. fail means it returned an error.
Documents inserted, changed, deleted, or returned by this step.
Mean wall-clock time of the passes, measured by the client from send to reply. The smaller line is the sample standard deviation of those passes.
Rows divided by the time. A step with no rows has no rate.
How many times this step chose an index. The number is the change in indexLookups. One command that uses an index counts as 1, however many documents come back.
How many times this step started a collection scan. The number is the change in collectionScans. One find that walks the collection counts as 1. The pages that walk reads are under Hit.
Kova only. Buffer-pool pages served from memory during the step. Reading a collection once counts one hit per page, so a scan of 1 can show many hits.
Kova only. Buffer-pool pages read from disk because they were not cached.
Kova only. WAL records this step appended. Many documents can share one record.
Extra detail for the step.
Mean wall-clock time of the passes. Each figure is labeled Kova or Mongo. The smaller line under a time is the sample standard deviation of that engine. The last line is Kova minus Mongo, then Kova's mean divided by Mongo's. That line says similar when the absolute difference of the means is within the standard deviation of the paired differences, and neither side wins. An oxide mark sits beside the faster time, and the whole row is tinted, only when Kova was faster by more than that. N/A means not available: MongoDB did not run that step.
Kova counts indexLookups: one command that chooses an index is 1, however many documents come back. Mongo counts keys examined: a range that walks 675 index keys is 675. The two figures count different events. N/A means not available: MongoDB did not report that counter.
Collection scans started during the step, on Kova and on Mongo. One find that walks the collection is 1. The pages that walk reads are under Hit, and Hit is Kova only. N/A means not available: MongoDB did not report that counter.
The same MQL steps on a MongoDB that was already listening. Only the database named benchmark is dropped there. The SQL table is Kova only.
An operation name opens the command. A question mark explains that column. A second point lookup runs after the first has already read the pages.