Query, predict & write speed (Beta)

The honest version first: for raw indexed search and filtering, a specialized engine like Elasticsearch (or a native C++ columnar store) will be faster than Aito, and we don't try to beat them at it. Aito's job on this axis is to be fast enough to serve interactive queries while carrying a prediction layer those engines don't have. This page states where Aito actually lands โ€” including a direct Elasticsearch head-to-head.

What we measure

  • Query / search latency โ€” filter and full-text queries over live data.
  • Predict / recommend / relate latency โ€” the inference layer; there is no equivalent in a search engine, so it's an Aito-only number, not a comparison.
  • Write / ingest โ€” how fast new rows become queryable.

Two neighbouring questions have their own pages: what the same database costs to hold is on Memory & disk footprint, and how all of it moves as the table grows is on How big can my database get?.

Query & search latency

Filter and full-text queries resolve out of the memory-mapped inverted index. Here is a direct head-to-head against Elasticsearch 8.15 on identical data and queries โ€” the same 100k flat invoice rows loaded into Aito v1, Aito v2 and Elasticsearch, each queried through one shared HTTP client so client overhead is identical. Every query shape is warmed before any of them is measured, and the measured rounds interleave the shapes, so no shape carries the stack's warm-up ramp for the others. Warm p50 latency (all three return the same hit counts):

QueryAito v1Aito v2Elasticsearch
Exact filter (company = N)6 ms6 ms4 ms
Full-text (description match)6 ms8 ms5 ms
Filter + full-text5 ms7 ms4 ms

Provenance. dataset invoice (100k flat rows) ยท warm p50 latency ยท Aito v1/v2 in-JVM vs Elasticsearch 8.15 in Docker on the same host ยท from booktest BookInvoiceEsBench es-head-to-head. Build-time tokens, not hand-typed.

Re-measured 2026-09-16. The previous version of this table warmed and timed one query shape at a time, which made the first shape measured absorb the warm-up ramp of the whole stack โ€” visible in print as a combined query that beat both of its own constituents. The measurement now interleaves the shapes, and the suite records the machine's load during each engine's phase so a contended run can be discarded rather than published. Taken on an idle machine; a second independent run agrees on every cell to within ~10%.

Read it straight: Elasticsearch is fastest on every query โ€” roughly 1.2โ€“1.7ร— quicker than Aito across the three shapes. That is the expected result and exactly the one we promised: a specialized search engine beats us on raw indexed search. Aito answers every shape in single-digit milliseconds โ€” interactive โ€” over the same index it uses for prediction.

The point isn't the milliseconds, it's that Aito stays interactive on the search axis and carries the calibrated-prediction layer a search engine doesn't have โ€” see the comparison that matters. These are single-machine committed baselines (Aito in-JVM, ES in Docker on the same host); read the ratio, not the absolute milliseconds.

Prediction latency โ€” v2 vs v1 (the part search engines don't have)

This is where the milliseconds buy something a search index can't do at all โ€” a calibrated, explained prediction over your live data, no model to train or serve. Here is the v2 (rep2 / CollectionDb) engine measured head-to-head against the production v1 (TableDb) engine on the 10M-row linked-invoice benchmark (predicting processor / acceptor / glCode). Both columns are now the same engine vintage measured the same way, so accuracy is shown for both; the scaling curve behind it is the ladder. Latency is the mean per-predict time across the evaluation run (it includes the one cold first query โ€” broken out separately below):

Predict targetv2 latencyv1 latencyv2 accuracy (end-to-end)v1 accuracy (end-to-end)
acceptor355 ms107 ms84.0%71.4%
glCode160 ms161 ms74.0%78.7%
processor396 ms213 ms73.0%65.8%

Provenance. dataset invoice (InvoiceData synthetic, linked) ยท scale 10M rows ยท v2 from booktest InvoicePerf perf-optimized-10M-rep2, v1 from perf-optimized-10M ยท single dev machine, hardware unspecified. Every cell is a build-time metric token resolved from the committed perf snapshots โ€” nothing here is hand-typed. Both engines: end-to-end top-1 over the same 511 held-out invoices, same corpus, same build, both at a 4 GB heap.

Read accuracy tightly and latency loosely. Accuracy here is deterministic โ€” the same build over the same held-out invoices reproduces it to every decimal, so a difference between the columns is a difference between the engines. The latency figures are single runs on a shared machine, and repeated measurement of identical code puts their run-to-run spread at up to ~8% (measured across three rounds of the whole ladder). Treat a latency gap smaller than that as noise, and the ratio between engines as more meaningful than the absolute milliseconds.

What this table says, and what it does not. v2 leads on both link targets โ€” the ones that resolve a reference to a row in another table (processor, acceptor) โ€” while v1 keeps glCode, the 32-class categorical. That split holds at every scale from 100k up in the ladder, so it is a property of the two engines rather than an artefact of one measurement. v1 remains several times faster per predict; that is the honest cost of v2's link-target accuracy, not a rounding difference.

An earlier version of this page withheld the v2 accuracy column, because the v2 10M snapshot was then a different engine vintage from the v1 column and computed over a different denominator โ€” printing them side by side would have compared two measurements of two builds. Both halves of that objection are now gone: every cell above and in the ladder comes from one build, one corpus and the same end-to-end denominator.

What the latency columns do support: at 10M rows, v2 is not faster on any target. It is level on glCode (160 ms vs 161 ms โ€” a 1% gap, well inside the ~8% noise band), and materially slower on both link targets: acceptor (355 ms vs 107 ms, ~3.3ร—) and processor (396 ms vs 213 ms, ~1.9ร—). Those cross-table links run through rep2's layered cursor stack, which is the active latency-tuning target. This is the trade stated plainly: v2 buys link-target accuracy with steady-state latency, and wins the cold start instead. Each predict ranks hundreds of candidates and returns a $why factor tree.

Warm-up โ€” first query, cold

The first query per target pays JIT, file-cache miss and dictionary first-touch. v2's memory-mapped columnar reads make cold-start faster on every target โ€” no big heap structures to materialize on first touch. The gap is widest on the two lower-cardinality targets (glCode ~14ร—, acceptor ~2.6ร—); on the high-cardinality processor target the engines are closer, both paying several seconds cold:

Predict targetv2 first queryv1 first query
acceptor1270 ms3287 ms
glCode219 ms2969 ms
processor6095 ms7679 ms

Provenance. the first-query (0-1) row of the same InvoicePerf perf-optimized-10M{,-rep2} snapshots; build-time tokens, not hand-typed. Both columns are the same build as above.

Memory & disk โ€” on its own page

The same 10M state, measured for footprint rather than speed, says v2 needs 597 MB of heap against v1's 2,276 MB โ€” about 3.8ร— leaner, and 2.3ร— smaller on disk. That is the number that decides how many tenants fit on a box, so it has its own page:

โ†’ Memory & disk footprint

Scaling

Accuracy and latency both improve with rows, and the full ladder โ€” five scales, both engines, one build โ€” now has its own page:

โ†’ Scaling: accuracy & latency vs. rows

The short version: both engines improve monotonically with rows, v2 leads both link targets from 100k up while v1 keeps the 32-class categorical, and per-query latency grows far slower than the table does. The 10M end-point of that ladder is the head-to-head above.

Writes

Writes are incremental: inserted rows are queryable and usable as prediction evidence immediately โ€” there is no separate reindex or model-retraining step (contrast the accuracy suites, where every ML baseline needs a full refit to absorb new data). Batched ingest uses a back-pressured write path with deferred segment merging so read latency stays stable under load.

Measured v2 ingest on the linked-invoice benchmark (batched 2,000-row commits through /api/v2):

Metricv2 ingest
Throughput21,716 rows/s
Per-commit latency (p50)90 ms
Per-commit latency (p90)107 ms

Provenance. dataset invoice ยท batched 2,000-row commits through /api/v2, deferMerge default ยท from booktest BookInvoiceIngestBench ingest-invoices-v2 ยท single dev machine, hardware unspecified. Build-time tokens, not hand-typed.

Deferred segment merging โ€” the default write path โ€” materially raises ingest throughput versus eager merging by batching the index rebuild instead of paying it on every commit. (The committed ingest snapshot measures only the deferred default; a tokenized eager-vs-deferred A/B is pending, so no fixed multiplier is quoted here.)

Like the scaling table, these are single-machine committed baselines generated from the BookInvoiceIngestBench snapshot โ€” read the magnitude (tens of thousands of rows/s, sub-100 ms commits), not the exact figure. Write throughput is markedly more sensitive to machine load than query latency: on a contended box the same ingest measures an order of magnitude slower, so the baseline is captured on an unloaded run.

The comparison that matters

Against a specialized search engine, expect Aito to be competitive but not category-leading on raw query speed, and incomparable on prediction โ€” because the search engine simply doesn't predict. The evaluation you're really making isn't "Aito vs Elasticsearch on QPS"; it's "one system that searches and predicts over live data with no training pipeline" vs "a fast search engine bolted to a separately-trained, separately-served ML stack." On the first axis you trade some raw speed; on the second you remove an entire moving part.

Beta. Every number in the query/Elasticsearch, prediction-latency, warm-up, scaling and write-throughput tables is a build-time metric token resolved from a committed InvoicePerf / BookInvoiceIngestBench / BookInvoiceEsBench snapshot, regenerated at docs-build time. Two gaps previously stated here are now closed: the memory/disk footprint has a committed whole-engine measurement, and the v2 10M scaling point is measured and in the tables โ€” the whole ladder was re-run on one build to get it. What remains open is stated on each page: the footprint is measured at one scale, not as a ladder. No number on this page is hand-typed, and none is carried over from an earlier engine vintage.