You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+23Lines changed: 23 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,6 +6,29 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
7
7
## [Unreleased]
8
8
9
+
### Fixed
10
+
-**Snapshot `CapacityExceeded` at scale** — `encode_state` rewritten from a
11
+
fixed `&mut [u8]` buffer to a growable `&mut Vec<u8>`. Snapshots above ~250K
12
+
records (any dimension) previously failed with `Kernel(CapacityExceeded)`
13
+
because the V6 schema added 10 bytes/record that the buffer-size formula did
14
+
not account for. Verified end-to-end at 1M records (515 MB snapshot in 1.2 s).
15
+
The encoder is now structurally incapable of this error. Stays `no_std`.
16
+
-**WAL loss on clean teardown** — added `impl Drop for Engine` and
17
+
`impl Drop for EventCommitter` to flush the batched write buffer on scope
18
+
exit. A clean shutdown could previously lose up to `flush_every` buffered
19
+
events; recovery tests found 0 events after a simulated crash.
20
+
21
+
### Added
22
+
-**IVF centroid auto-scaling** (`n_list = max(16, sqrt(N))`, `n_probe = max(1, sqrt(n_list))`) — fixes a 153× QPS regression from 10K to 1M records. Centroids now scale with dataset size so average bucket size stays O(sqrt(N)) and scan cost is O(sqrt(N)) not O(N). Manual override via `VALORI_IVF_N_LIST` / `VALORI_IVF_N_PROBE` disables auto-scaling. Added `IvfIndex::needs_rebuild(count)` hook (returns true when online inserts exceed 2× the build size).
23
+
-**`encode_capacity_hint(state)`** — V6-correct pre-allocation estimate so the
24
+
snapshot `Vec` avoids repeated reallocation on the hot path.
|`VALORI_AUTH_TOKEN`| — | Bearer token (omit = no auth) |
231
-
|`VALORI_INDEX`| brute |`brute` or `hnsw`|
231
+
|`VALORI_INDEX`| brute |`brute`, `hnsw`, or `ivf`|
232
+
|`VALORI_IVF_N_LIST`| auto | IVF centroid count. Absent = auto-scale: `max(16, sqrt(N))` computed at each `build()`. Setting this disables auto-scale. |
233
+
|`VALORI_IVF_N_PROBE`| auto | IVF probe count. Absent = auto-scale: `max(1, sqrt(n_list))`. Setting this disables auto-scale. |
232
234
|`VALORI_DECAY_HALF_LIFE_SECS`| — | Phase C4.1 default decay half-life for search ranking; per-request `decay_half_life_secs` overrides. Omit/0 = no decay |
> **Batch size warning:**`insert_batch` with fewer than 100 records is **slower than a plain `insert` loop** — per-call overhead dominates at small sizes. Always use batches of ≥ 100; the sweet spot is 1,000–10,000.
153
+
154
+
### HNSW search latency by embedding model (10K records, k=10)
155
+
156
+
| Model | Dim | HNSW p50 | HNSW QPS | Brute p50 | Brute QPS |
157
+
|---|---|---|---|---|---|
158
+
| baseline / custom | 128 | 0.050 ms | 19,759 q/s | 1.224 ms | 810 q/s |
159
+
| nomic-embed-text · all-MiniLM-L6-v2 | 384 | 0.146 ms | 4,486 q/s | 3.329 ms | 273 q/s |
| OpenAI ada-002 · text-embedding-3-small | 1,536 | 0.523 ms | 1,897 q/s | 14.923 ms | 66 q/s |
162
+
163
+
> **Index selection warning:** Brute force is O(N) — latency grows linearly with dataset size. It becomes unviable above ~50K records at any dimension. **HNSW is mandatory for production read-heavy workloads above 50K records.** Build cost is paid once and survives snapshot/restore; search stays sub-millisecond regardless of dataset size.
164
+
165
+
### Search latency vs dataset size (HNSW, dim=128, k=10)
166
+
167
+
| Records | p50 | p99 | QPS |
168
+
|---|---|---|---|
169
+
| 1,000 | 0.05 ms | — |~20,000 q/s |
170
+
| 10,000 | 0.05 ms | 0.069 ms | 19,759 q/s |
171
+
| 1,000,000 |**0.107 ms**| 0.138 ms |**9,199 q/s**|
172
+
173
+
→ **Sub-millisecond search at 1 million records.**
174
+
175
+
### Search latency vs dataset size (bruteforce, dim=128, k=10)
176
+
177
+
| Records | p50 | p95 | p99 | QPS |
178
+
|---|---|---|---|---|
179
+
| 1,000 | 0.129 ms | 0.131 ms | 0.135 ms | 7,820 q/s |
180
+
| 10,000 | 1.224 ms | 1.285 ms | 1.354 ms | 810 q/s |
181
+
| 50,000 | 10.129 ms | 10.735 ms | 11.336 ms | 98 q/s |
182
+
| 1,000,000 | 247.815 ms | 288.795 ms | 308.291 ms | 3 q/s |
183
+
184
+
### Index comparison @ 1 million records (dim=128, k=10)
185
+
186
+
| Index | Build time | p50 | p99 | QPS |
187
+
|---|---|---|---|---|
188
+
|**HNSW**| 4.4 min (one-time) |**0.107 ms**|**0.138 ms**|**9,199 q/s**|
189
+
| IVF | 28 s | 58.35 ms | 66.05 ms | 16 q/s |
190
+
| Brute force | 27 s | 247.41 ms | 297.01 ms | 4 q/s |
191
+
192
+
### Snapshot timing
193
+
194
+
| Records | Dim | Size |`snapshot()`|`restore()`|`save_snapshot()`|
195
+
|---|---|---|---|---|---|
196
+
| 10,000 | 128 | 5.2 MB | 2.2 ms | 4.3 ms | 4.7 ms |
197
+
| 10,000 | 384 | 14.9 MB | 5.9 ms | 6.0 ms | 12.4 ms |
198
+
| 10,000 | 768 | 29.6 MB | 10.0 ms | 16.3 ms | 20.6 ms |
199
+
| 10,000 | 1,536 | 58.9 MB | 18.8 ms | 29.5 ms | 44.7 ms |
200
+
| 50,000 | 128 | 25.8 MB | 10.1 ms | 21.6 ms | 26.7 ms |
201
+
202
+
### Batch size sweet spot (dim=128, bruteforce, 10K total records)
203
+
204
+
| Batch size | Throughput |
205
+
|---|---|
206
+
| 1 (single inserts) | 2,512 rec/s |
207
+
| 10 | 1,936 rec/s ⚠️ slower than single |
208
+
| 100 | 14,561 rec/s |
209
+
| 500 | 60,805 rec/s |
210
+
| 1,000 | 95,147 rec/s |
211
+
|**10,000**|**174,963 rec/s**|
212
+
213
+
---
214
+
103
215
## Get Started
104
216
105
217
> **New contributor?**`bash dev-setup.sh` — one script installs Rust, the wasm32 target, Python SDK, and UI deps with OS detection and version gates. See [Build from Source](#build-from-source) and [CONTRIBUTING.md](CONTRIBUTING.md).
Copy file name to clipboardExpand all lines: benchmarks/README.md
+26Lines changed: 26 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,10 +12,36 @@ Built in direct response to the 2026-06-12 review (Mayur, Rahul). Three asks, th
12
12
13
13
| Script | What it measures | Run it |
14
14
|---|---|---|
15
+
|[`local_perf.py`](local_perf.py)| Insert throughput, search latency, index comparison, snapshot — up to 1M records, no server required |`python3 benchmarks/local_perf.py --million`|
15
16
|[`run_benchmark.py`](run_benchmark.py)| Three-arm RAG quality (float32 vs Q16.16 vs Q16.16+graph) |`python3 benchmarks/run_benchmark.py`|
16
17
|[`multi_arch_hash.py`](multi_arch_hash.py)| Identical BLAKE3 state hash across CPU architectures |`python3 benchmarks/multi_arch_hash.py --url http://localhost:3000`|
17
18
|[`q16_precision.py`](q16_precision.py)| Recall@10 of Q16.16 vs float32 ground truth at 384/768/1536/3072 dims |`python3 benchmarks/q16_precision.py --dim 384 [--st] [--openai]`|
0 commit comments