Core CI / core (ubuntu-latest) (push) Failing after 3m27s
Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Successful in 5m45s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s
229 lines
13 KiB
Markdown
229 lines
13 KiB
Markdown
# CPU retrieval performance
|
|
|
|
`examples/performance.rs` builds an explicitly synthetic SQLite corpus using the
|
|
real core schema and normal FTS triggers. It measures warm core retrieval only;
|
|
there is no filesystem scanning, document extraction, model inference, GUI
|
|
rendering, or GPU provider in this benchmark.
|
|
|
|
The default smoke fixture contains 10,000 file records and 1,000 searchable
|
|
document/passages. The acceptance fixture contains 1,000,000 file records and
|
|
100,000 searchable documents, one paragraph per document. The remaining records
|
|
have filenames but no content. Names and documents are synthetic; they do not
|
|
represent a realistic corpus distribution, so passing these measurements alone
|
|
would not establish production performance.
|
|
|
|
```powershell
|
|
cargo run --release -p searchi-core --example performance -- --files 1000000 --documents 100000 --samples 20 --db target/benchmarks/full.db
|
|
```
|
|
|
|
Use a new database path when seeding. The harness refuses to overwrite an
|
|
existing database. To compare engine versions on the same corpus, use `--reuse true`;
|
|
this validates file/document counts and synthetic root ownership before opening
|
|
and migrating the existing fixture. It records migration time separately and
|
|
reports zero seed time. It seeds at most 5,000 records per transaction, checkpoints
|
|
the WAL, then performs three warmups and 20 measured queries per case. Percentiles
|
|
use the nearest-rank convention; with 20 samples, p95 is the 19th sorted sample.
|
|
The first execution of each query is recorded separately before the remaining
|
|
two warmups; this is first use of that query on the current engine, not a cold
|
|
OS disk-cache test. Timing uses a monotonic clock. Results include committed database size and fixture
|
|
seed time, which must not be described as document extraction throughput.
|
|
|
|
Cases cover an exact filename near the end of the corpus, a broad filename
|
|
substring, a unique content token, and a broad content token. All queries use the
|
|
public search API with limit 20, including actual source-passage selection. Target
|
|
p95 is below 50 ms for filenames and below 150 ms for keyword search. A result
|
|
above either target is explicitly marked as a failure. Database file bytes exclude
|
|
temporary WAL/SHM sidecars and process memory.
|
|
|
|
## Reference environment
|
|
|
|
- Date: 2026-10-12.
|
|
- Windows VM: Windows 11 Pro, version 10.0.26300.
|
|
- CPU name exposed by VM: AMD Ryzen 7 7800X3D 8-Core Processor.
|
|
- VM allocation: 4 logical processors, 10,666,553,344 bytes RAM (about 9.93 GiB).
|
|
- Rust release build, bundled SQLite, CPU only.
|
|
- Toolchain: rustc 1.99.0 (b940084d7 2026-09-28), cargo 1.99.0.
|
|
- Engine baseline: `a9b6592330d88745eadf2a748bb3a65f62830590`, schema 4.
|
|
- Storage device and host scheduling contention are not characterized.
|
|
|
|
Run results are stored in `results/`. Linux and macOS measurements are pending.
|
|
No cross-platform acceptance claim can be inferred from the Windows VM results.
|
|
|
|
## Baseline result
|
|
|
|
The full acceptance fixture fails all four latency targets. This is a verified
|
|
performance gap, not a production-ready retrieval result.
|
|
|
|
| Query case | p50 | p95 | p95 target | Result |
|
|
| --- | ---: | ---: | ---: | --- |
|
|
| Exact filename | 404.76 ms | 633.82 ms | <50 ms | Failed |
|
|
| Broad filename substring | 3,385.76 ms | 4,131.48 ms | <50 ms | Failed |
|
|
| Unique content token | 311.63 ms | 355.78 ms | <150 ms | Failed |
|
|
| Broad keyword | 2,186.24 ms | 2,746.91 ms | <150 ms | Failed |
|
|
|
|
The 1M/100K synthetic fixture took 277.23 seconds to seed and its checkpointed
|
|
database occupies 309,338,112 bytes (about 295 MiB). Smaller 10K/1K smoke results
|
|
passed the same targets, showing why small-corpus tests must not be used as proof
|
|
of scalability. Neither seed time measures document extraction throughput.
|
|
|
|
The baseline implementation scanned file metadata for substring matching and used
|
|
per-result passage lookups. Its measured behavior warranted a dedicated filename
|
|
substring index, candidate-first FTS retrieval, and batched passage selection.
|
|
The following section records the resulting implementation and measurements.
|
|
|
|
## Optimized result, schema 7
|
|
|
|
The unchanged full fixture was migrated and reused. All four **warm** target
|
|
measurements pass in this Windows CPU run. This does not establish production
|
|
acceptance for all workloads or operating systems.
|
|
|
|
| Query case | Baseline p95 | Optimized p50 | Optimized p95 | First use | Warm target |
|
|
| --- | ---: | ---: | ---: | ---: | --- |
|
|
| Exact filename | 633.82 ms | 11.66 ms | 13.17 ms | 279.73 ms | Passed |
|
|
| Broad filename substring | 4,131.48 ms | 0.39 ms | 1.68 ms | 3.34 ms | Passed |
|
|
| Unique content token | 355.78 ms | 0.28 ms | 0.39 ms | 50.08 ms | Passed |
|
|
| Broad keyword | 2,746.91 ms | 97.92 ms | 111.54 ms | 101.35 ms | Passed |
|
|
|
|
The updated checkpointed database is 634,449,920 bytes (about 605 MiB). The
|
|
improvement trades additional indexes and disk space for retrieval latency.
|
|
The initial schema-4-to-5 backfill took 19.65 seconds on this fixture, as recorded
|
|
in the intermediate report. The final report's 3.84 ms `migration_seconds` is an
|
|
already-current-schema open, not a measurement of the initial index backfill.
|
|
Indexed equality, trigram candidate retrieval, content-only FTS, and a single
|
|
batched source lookup replaced full metadata joins and per-result FTS scans.
|
|
Rarest-trigram vocabulary counts are cached in the reader engine. The first-use
|
|
exact result exceeds the warm target and is explicitly retained as an outstanding
|
|
latency issue; opening a fresh reader for every query discards this cache.
|
|
|
|
`windows-cpu-full-optimized-v7.json` is the final measurement. Earlier schema 5/6
|
|
reports are intermediate experiments and retain failed exact-query measurements.
|
|
The final run did not overlap another benchmark process. Dedicated host isolation
|
|
is still not established. Query distribution and filters are not represented by
|
|
this four-case fixture; restrictive broad filters and sub-three-character searches
|
|
remain potentially slow, while correctness tests ensure late matching files are
|
|
not lost through arbitrary pre-filter caps.
|
|
|
|
## Actual first foreground inputs, schema 8
|
|
|
|
The schema-8 run persists trigram frequency statistics in background preparation
|
|
before allowing the measured inputs. Preparation took **17.21 seconds** and
|
|
loaded 1,268 frequency entries. That time is reported separately and is not
|
|
subtracted from a foreground query. The same 1M/100K fixture was reused.
|
|
|
|
The harness measures 20 different exact filenames, spread across the corpus,
|
|
with no preceding query warmups. It then measures 20 different filenames, each
|
|
on a newly opened reader without calling `prewarm` again, using persisted stats.
|
|
This second set includes reader-open time. Both sets validate exact-match priority
|
|
and record every raw query and duration. They are disjoint from each other and
|
|
from the four-case exact query for this full fixture.
|
|
|
|
| Distinct first-input set | p50 | p95 | Actual very first input | Samples ≥50 ms |
|
|
| --- | ---: | ---: | ---: | ---: |
|
|
| Prepared reader, 20 names | 13.96 ms | 18.97 ms | **98.49 ms** | 1/20 |
|
|
| Fresh readers, 20 other names | 16.78 ms | 22.73 ms | **103.57 ms** | 1/20 |
|
|
|
|
The p95 filename targets pass, but the very first zero-heavy filename in each
|
|
set fails the 50 ms target. The nearest-rank p95 excludes one maximum sample
|
|
when there are 20 samples, so p95 alone must not be used to claim that the actual
|
|
first input is always fast. Explicit first-sample flags and maximum durations
|
|
are included in the report, derived directly from the recorded raw samples.
|
|
|
|
| Existing case, first execution before its warmups | First input | First target | Repeated warm p95 |
|
|
| --- | ---: | --- | ---: |
|
|
| Exact filename at end of corpus | 16.31 ms | Passed <50 ms | 13.80 ms |
|
|
| Broad filename substring | 13.01 ms | Passed <50 ms | 0.47 ms |
|
|
| Unique content token | 14.07 ms | Passed <150 ms | 0.49 ms |
|
|
| Broad keyword | **264.53 ms** | **Failed <150 ms** | 145.19 ms |
|
|
|
|
These measurements expose remaining first-input work even after frequency
|
|
preparation. Zero-heavy names produce larger candidate sets, and broad content
|
|
retrieval has a first-use penalty. This report does not establish the requested
|
|
fast-first-input behavior as complete. It also does not represent a cold OS
|
|
page-cache test: background preparation and previous operations can warm disk
|
|
pages. Host workload isolation remains unverified. The retained report is
|
|
`windows-cpu-full-prepared-v8.json`; engine code was not changed during this run.
|
|
|
|
## Background representative search and restart cost, schema 9
|
|
|
|
Schema 9 refreshes frequency statistics only when filename/path membership is
|
|
dirty. It also runs representative searches in background. The report explicitly
|
|
records the actual five background inputs from this corpus: `invoice_0000000.txt`,
|
|
`invoice_0999999.txt`, `Supplier`, `termination`, and `cancellation`. Consequently
|
|
the later first foreground executions of overlapping inputs are prepared inputs,
|
|
not never-before-executed queries. This behavior is intentional application
|
|
preparation, rather than an undisclosed benchmark warmup.
|
|
|
|
Initial preparation took **17.723 seconds**. A second `prewarm()` on a newly
|
|
opened reader took **0.396 seconds**, reusing clean persisted statistics. That
|
|
second call occurred only after all foreground measurements, so it could not
|
|
improve their recorded first timings. Startup time is amortized when names have
|
|
not changed; refresh after mutations still has an explicit background cost.
|
|
|
|
| Twenty distinct foreground names | p50 | p95 | Very first name | ≥50 ms |
|
|
| --- | ---: | ---: | ---: | ---: |
|
|
| Prepared reader | 12.75 ms | 14.40 ms | **85.38 ms** | 1/20 |
|
|
| Newly reopened readers | 16.31 ms | 31.42 ms | **100.07 ms** | 1/20 |
|
|
|
|
The remaining zero-heavy filename outliers fail the first-input target even
|
|
after representative preparation. Repeating that representative query in
|
|
background does not eliminate its candidate-processing cost. The 20 distinct
|
|
query sets remain unchanged from schema 8, and every raw sample and explicit
|
|
first/max failure flag is retained.
|
|
|
|
| Existing case | First foreground | Repeated p95 | Target |
|
|
| --- | ---: | ---: | --- |
|
|
| Exact filename at corpus end | 13.48 ms | 13.22 ms | Passed <50 ms |
|
|
| Filename substring | 3.20 ms | 0.43 ms | Passed <50 ms |
|
|
| Unique content token | 2.57 ms | 0.53 ms | Passed <150 ms |
|
|
| Broad keyword | 99.10 ms | 106.20 ms | Passed <150 ms |
|
|
|
|
Background initialization fixes the observed broad-keyword first-use penalty,
|
|
but the full fast-first-input requirement remains incomplete because of the
|
|
filename outliers. Further algorithmic filename work is required; the report
|
|
does not infer that preparation makes every unseen query fast. This Windows
|
|
CPU-only run is stored in `windows-cpu-full-prepared-v9.json`.
|
|
|
|
## Positional candidate correction and finalist projection
|
|
|
|
Extending the rarest trigram through the entire suffix regressed retrieval:
|
|
the unchanged 20-name sets had p95 **136.60/129.17 ms**, and the first reopened
|
|
zero-heavy name took **554.32 ms**. That rejected experiment remains recorded in
|
|
`windows-cpu-full-suffix-anchor.json`. A long phrase can initialize dense suffix
|
|
postings even when its first term is rare.
|
|
|
|
The replacement grows a positional anchor only through neighboring grams within
|
|
a bounded frequency factor, with a maximum of 32 Unicode characters. Full literal
|
|
verification and filter-before-limit completeness remain unchanged. Temporary
|
|
lane profiling separated cheap indexed equality (about 0.06 ms in the diagnostic
|
|
run) from first-use broad-prefix filename retrieval (about 67 ms). Generic
|
|
alphabetic tokens derived from representative names now initialize those common
|
|
postings in background. Unfiltered content searches rank compact IDs before
|
|
materializing finalist paths/snippets; filtered ranking is kept complete.
|
|
|
|
The final unchanged 1M/100K corpus run uses these disclosed background inputs:
|
|
first/last filenames, `invoice`, `txt`, and the first document words `Supplier`,
|
|
`termination`, and `cancellation`. Clean-statistics preparation took **0.709 s**;
|
|
a subsequent new-reader preparation took **0.264 s**. Earlier initial dirty
|
|
statistics refresh still took roughly 17 seconds; this run does not erase that
|
|
startup/refresh cost.
|
|
|
|
| Twenty distinct first inputs | p95 | Very first | Maximum | Samples ≥50 ms |
|
|
| --- | ---: | ---: | ---: | ---: |
|
|
| Prepared reader | 18.15 ms | 6.14 ms | 22.94 ms | 0/20 |
|
|
| Newly reopened readers, including open | 30.86 ms | 30.86 ms | 34.28 ms | 0/20 |
|
|
|
|
| Existing case | First foreground | Repeated p95 | Target |
|
|
| --- | ---: | ---: | --- |
|
|
| Exact filename at corpus end | 4.37 ms | 3.77 ms | Passed <50 ms |
|
|
| Filename substring | 14.18 ms | 1.23 ms | Passed <50 ms |
|
|
| Unique content token | 10.06 ms | 0.72 ms | Passed <150 ms |
|
|
| Broad keyword | 114.44 ms | 117.88 ms | Passed <150 ms |
|
|
|
|
All measured first-input samples and repeated-query p95 values pass in this
|
|
Windows CPU run. The retained final report is
|
|
`windows-cpu-full-candidate-first-final.json`, with every raw filename/sample.
|
|
This establishes the measured fixture result, not a guarantee for all queries,
|
|
filters, corpus distributions, cold OS caches, or other operating systems. The
|
|
intermediate density-only reports retain remaining failures and are not promoted
|
|
as final acceptance evidence.
|