@soulcraft/cor 3.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +328 -0
  3. package/dist/aggregation/NativeAggregationEngine.d.ts +118 -0
  4. package/dist/aggregation/NativeAggregationEngine.js +186 -0
  5. package/dist/cli.d.ts +12 -0
  6. package/dist/cli.js +236 -0
  7. package/dist/graph/GraphAccelerationAdapter.d.ts +153 -0
  8. package/dist/graph/GraphAccelerationAdapter.js +326 -0
  9. package/dist/graph/NativeGraphAdjacencyIndex.d.ts +286 -0
  10. package/dist/graph/NativeGraphAdjacencyIndex.js +944 -0
  11. package/dist/hnsw/AdaptiveDiskAnnModeSelector.d.ts +187 -0
  12. package/dist/hnsw/AdaptiveDiskAnnModeSelector.js +313 -0
  13. package/dist/hnsw/NativeDiskAnnWrapper.d.ts +489 -0
  14. package/dist/hnsw/NativeDiskAnnWrapper.js +1456 -0
  15. package/dist/index.d.ts +28 -0
  16. package/dist/index.js +29 -0
  17. package/dist/legacyLayoutGuard.d.ts +93 -0
  18. package/dist/legacyLayoutGuard.js +237 -0
  19. package/dist/license/constants.d.ts +30 -0
  20. package/dist/license/constants.js +30 -0
  21. package/dist/license/onlineValidator.d.ts +43 -0
  22. package/dist/license/onlineValidator.js +225 -0
  23. package/dist/license/types.d.ts +39 -0
  24. package/dist/license/types.js +5 -0
  25. package/dist/license.d.ts +64 -0
  26. package/dist/license.js +165 -0
  27. package/dist/migration/MigrationCoordinator.d.ts +171 -0
  28. package/dist/migration/MigrationCoordinator.js +249 -0
  29. package/dist/migration/indexEpochGuard.d.ts +163 -0
  30. package/dist/migration/indexEpochGuard.js +202 -0
  31. package/dist/native/NativeEmbeddingEngine.d.ts +79 -0
  32. package/dist/native/NativeEmbeddingEngine.js +318 -0
  33. package/dist/native/NativeRoaringBitmap32.d.ts +114 -0
  34. package/dist/native/NativeRoaringBitmap32.js +221 -0
  35. package/dist/native/ffi.d.ts +20 -0
  36. package/dist/native/ffi.js +48 -0
  37. package/dist/native/idMapperTestSupport.d.ts +67 -0
  38. package/dist/native/idMapperTestSupport.js +112 -0
  39. package/dist/native/index.d.ts +49 -0
  40. package/dist/native/index.js +87 -0
  41. package/dist/native/napi.d.ts +21 -0
  42. package/dist/native/napi.js +88 -0
  43. package/dist/native/types.d.ts +1298 -0
  44. package/dist/native/types.js +16 -0
  45. package/dist/plugin.d.ts +50 -0
  46. package/dist/plugin.js +388 -0
  47. package/dist/providerContracts.d.ts +277 -0
  48. package/dist/providerContracts.js +38 -0
  49. package/dist/resource/OsMemoryProbe.d.ts +175 -0
  50. package/dist/resource/OsMemoryProbe.js +206 -0
  51. package/dist/resource/ResourceManager.d.ts +491 -0
  52. package/dist/resource/ResourceManager.js +960 -0
  53. package/dist/utils/ColumnManifest.d.ts +97 -0
  54. package/dist/utils/ColumnManifest.js +129 -0
  55. package/dist/utils/NativeColumnStore.d.ts +284 -0
  56. package/dist/utils/NativeColumnStore.js +685 -0
  57. package/dist/utils/NativeMetadataIndex.d.ts +882 -0
  58. package/dist/utils/NativeMetadataIndex.js +2631 -0
  59. package/dist/utils/NativeUnifiedCache.d.ts +87 -0
  60. package/dist/utils/NativeUnifiedCache.js +273 -0
  61. package/dist/utils/binaryIdMapperFactory.d.ts +59 -0
  62. package/dist/utils/binaryIdMapperFactory.js +94 -0
  63. package/dist/utils/collation.d.ts +30 -0
  64. package/dist/utils/collation.js +40 -0
  65. package/dist/utils/columnStoreTypes.d.ts +161 -0
  66. package/dist/utils/columnStoreTypes.js +29 -0
  67. package/dist/utils/nativeBinaryEntityIdMapper.d.ts +211 -0
  68. package/dist/utils/nativeBinaryEntityIdMapper.js +381 -0
  69. package/dist/utils/nativeEntityIdMapper.d.ts +111 -0
  70. package/dist/utils/nativeEntityIdMapper.js +170 -0
  71. package/docs/ADR-002-diskann-100-percent-rust.md +294 -0
  72. package/docs/ADR-003-semantic-time-travel.md +100 -0
  73. package/docs/aggregation.md +251 -0
  74. package/docs/comparison.md +162 -0
  75. package/docs/deployment-limits.md +87 -0
  76. package/docs/diskann.md +184 -0
  77. package/docs/migration-3.0.md +396 -0
  78. package/docs/performance-budget.md +274 -0
  79. package/docs/performance.md +117 -0
  80. package/docs/scaling.md +439 -0
  81. package/docs/snapshot-safety.md +246 -0
  82. package/docs/u64-id-space.md +214 -0
  83. package/docs/verification-report.md +670 -0
  84. package/native/brainy-native.node +0 -0
  85. package/native/index.d.ts +3916 -0
  86. package/package.json +92 -0
  87. package/scripts/migrate-cortex-2x-to-3x.mjs +970 -0
@@ -0,0 +1,274 @@
1
+ ---
2
+ title: Performance Budget
3
+ slug: cor/performance-budget
4
+ public: true
5
+ category: cor
6
+ template: concept
7
+ order: 11
8
+ description: The latency budget cor commits to for find() queries at 1M, 10M, and 100M scale, enforced by CI regression gates and grounded in the triple-intelligence workload that defines the brainy + cor stack.
9
+ next:
10
+ - cor/snapshot-safety
11
+ - cor/verification-report
12
+ ---
13
+
14
+ # Performance Budget
15
+
16
+ This document is a contract. The latency targets below are
17
+ CI-enforced regression gates, not aspirations. Code that fails a
18
+ gate does not ship.
19
+
20
+ ## Why a budget matters
21
+
22
+ Brainy + cor is the only database that answers a single query
23
+ combining:
24
+
25
+ - **Metadata filter** — structured `where` over indexed fields
26
+ (LSM metadata index with Roaring bitmap evaluation)
27
+ - **Graph traversal** — multi-hop relationship walks with verb-type
28
+ selectivity (graph adjacency LSM with verb endpoint lookups)
29
+ - **Vector similarity** — semantic search over the entire corpus
30
+ (Adaptive DiskANN: Mode 1 for in-memory recall, hybrid for
31
+ PQ-compressed billion-scale)
32
+
33
+ No pure vector database (Pinecone, Weaviate, Qdrant), pure graph
34
+ database (Neo4j, Memgraph), or document database (MongoDB,
35
+ Elasticsearch) does this. PostgreSQL with pgvector gets two of
36
+ three with a multi-second join cost. The combined query is the
37
+ differentiator and it is only differentiating if it is sub-
38
+ millisecond at billion scale.
39
+
40
+ The budget locks in that property as a release criterion.
41
+
42
+ ## The budget
43
+
44
+ All measurements are p50 / p99 over 100 queries on a single 32-
45
+ core box (workstation tier: i9-14900HX / 64 GB / NVMe Gen 4;
46
+ billion-tier: bxl9000 / Ryzen 9 7950X3D / 184 GB / NVMe). Queries
47
+ are drawn from a representative workload mix described in
48
+ *Methodology* below.
49
+
50
+ ### Read path budget
51
+
52
+ | Query shape | 1 M entities | 10 M | 100 M |
53
+ |---|---:|---:|---:|
54
+ | Filter-only `find({where})` p50 | ≤ 0.5 ms | ≤ 1 ms | ≤ 3 ms |
55
+ | Filter-only p99 | ≤ 2 ms | ≤ 5 ms | ≤ 15 ms |
56
+ | Vector-only `find({vector})` p50 | ≤ 1 ms | ≤ 2 ms | ≤ 5 ms |
57
+ | Vector-only p99 | ≤ 3 ms | ≤ 6 ms | ≤ 15 ms |
58
+ | Hybrid `find({where, vector})` p50 | ≤ 2 ms | ≤ 4 ms | ≤ 10 ms |
59
+ | Hybrid p99 | ≤ 8 ms | ≤ 15 ms | ≤ 30 ms |
60
+ | 1-hop `find({where, hop})` p50 | ≤ 1.5 ms | ≤ 3 ms | ≤ 8 ms |
61
+ | 2-hop `find({where, hop, hop})` p50 | ≤ 3 ms | ≤ 6 ms | ≤ 15 ms |
62
+ | 2-hop p99 | ≤ 12 ms | ≤ 25 ms | ≤ 50 ms |
63
+ | Hybrid + 2-hop p50 | ≤ 5 ms | ≤ 10 ms | ≤ 25 ms |
64
+
65
+ ### Cross-subsystem ID resolution budget
66
+
67
+ `find()` returns UUID strings to JS, so every result row pays the
68
+ int→UUID resolution. Batched APIs are mandatory; per-call APIs are
69
+ not part of the hot path.
70
+
71
+ | Operation | Budget |
72
+ |---|---:|
73
+ | `getOrAssignBatch(uuids, len=1K)` | ≤ 100 μs |
74
+ | `intsToUuidsBatch(ints, len=1K)` | ≤ 80 μs |
75
+ | `intsToUuidsBatch(ints, len=10K)` | ≤ 500 μs |
76
+ | `getNeighborsBatch(int_ids, len=1K)` | ≤ 150 μs |
77
+ | `getVerbsBatch(verb_ids, len=1K)` | ≤ 120 μs |
78
+
79
+ ### Write path budget
80
+
81
+ | Operation | 1 M | 10 M | 100 M |
82
+ |---|---:|---:|---:|
83
+ | Single `add(noun)` p50 | ≤ 0.3 ms | ≤ 0.3 ms | ≤ 0.5 ms |
84
+ | Single `add(verb)` p50 | ≤ 0.2 ms | ≤ 0.2 ms | ≤ 0.3 ms |
85
+ | Batched `add` (1K) p50 | ≤ 20 ms | ≤ 20 ms | ≤ 30 ms |
86
+ | Sustained insert throughput (32-core box) | ≥ 50 K/s | ≥ 30 K/s | ≥ 20 K/s |
87
+
88
+ ### Recall floor
89
+
90
+ Recall is a correctness property, but it has a budget too: a query
91
+ that returns the wrong answer in 0.5 ms is worse than one that
92
+ takes 1 ms and returns the right one.
93
+
94
+ | Workload | Mode | Recall@10 floor |
95
+ |---|---|---:|
96
+ | Production embeddings (dim ≥ 384, clustered) | Mode 1 in-memory | ≥ 0.97 |
97
+ | Production embeddings | Mode 2 hybrid (PQ16) | ≥ 0.95 |
98
+ | SIFT canonical (dim 128, BIGANN ground truth) | Mode 1 in-memory | ≥ 0.98 |
99
+ | SIFT canonical | Mode 2 hybrid | ≥ 0.55 (PQ16 is adversarial here — operators tune via `recall` preset) |
100
+
101
+ ## How the budget is enforced
102
+
103
+ Every gate in the table corresponds to a regression test in
104
+ `src/native/findPerfGates.test.ts` (to be added; tracked under
105
+ the Cor 3.0 release plan). The test pattern follows the
106
+ existing `lsmPerfGates.test.ts`:
107
+
108
+ 1. Build a corpus at the target scale with a representative
109
+ field distribution (50 fields, mixed cardinality, ~10 verbs
110
+ per noun, dim 384 vectors with clustered structure).
111
+ 2. Warm the page cache with one pass over the query set.
112
+ 3. Run 100 queries per shape, record p50 and p99.
113
+ 4. Assert p50 ≤ budget × 1.0 and p99 ≤ budget × 1.2 (20 %
114
+ headroom on p99 absorbs CI variance).
115
+
116
+ A red gate is a release blocker. The test prints the measured
117
+ distribution so the regression is bisectable to a commit.
118
+
119
+ ## Methodology
120
+
121
+ ### Workload mix
122
+
123
+ The representative workload is a 70 / 20 / 10 split:
124
+
125
+ - 70 % filter-driven queries (`where` with 1-3 predicates,
126
+ selectivity from 0.1 % to 10 %)
127
+ - 20 % vector-driven queries (vector search with optional
128
+ metadata pre-filter)
129
+ - 10 % graph-driven queries (1- or 2-hop walks with verb-type
130
+ selectivity)
131
+
132
+ The mix tracks the access pattern Soulcraft Memory observes in
133
+ production: most queries are structured browse / filter; vector
134
+ search is the discovery path; graph hops are the relationship
135
+ expansion.
136
+
137
+ ### Hardware floor
138
+
139
+ The budget is anchored on the cor 3.0 design floor — a 32-
140
+ thread Intel Core i9-14900HX with 64 GB DDR5 and NVMe Gen 4 SSD.
141
+ That's roughly a $2K commodity workstation; production targets
142
+ sit on top of it. The bxl9000 measurements (Ryzen 9 7950X3D, 184
143
+ GB) are the billion-tier ceiling. Numbers from cloud VMs are
144
+ typically 1.3–1.8× slower for the same work because of memory
145
+ bandwidth and NVMe queue depth.
146
+
147
+ ### What "p50" and "p99" mean here
148
+
149
+ - **p50** is the median measurement over 100 query repetitions
150
+ with a warm page cache. It represents the steady-state cost.
151
+ - **p99** is the 99th percentile and represents the worst typical
152
+ query — page-cache miss, garbage collection pause, or a
153
+ background msync.
154
+ - We do not publish a p99.9 budget. p99.9 measurements are
155
+ dominated by external noise (kernel scheduling, NVMe firmware
156
+ latency spikes) and are not reproducible across runs.
157
+
158
+ ## The hot paths inside `find()`
159
+
160
+ A unified `find({where, vector, hop, orderBy, limit, offset})`
161
+ query executes against three cor subsystems in sequence. Each
162
+ subsystem has a sub-budget that rolls into the totals above.
163
+
164
+ 1. **Filter pushdown** — `metadataIndex.getIdsForFilter(field,
165
+ value)` returns a Roaring bitmap of matching entity ints. AND
166
+ / OR / NOT combinators are bitmap operations. Sub-budget:
167
+ ≤ 0.3 ms for a 3-predicate AND at 100 M.
168
+ 2. **Vector search** — `NativeDiskAnnWrapper.searchWithFilter`
169
+ walks the Vamana graph, prefilters candidates against the
170
+ metadata bitmap, scores by distance. Sub-budget: ≤ 1 ms at
171
+ 1 M (Mode 1), ≤ 4 ms at 100 M (hybrid).
172
+ 3. **Graph hop expansion** — `graphIndex.getNeighbors(int_id,
173
+ verb_type)` returns a Roaring bitmap of neighbor ints. Sub-
174
+ budget: ≤ 0.3 ms per hop at 100 M.
175
+ 4. **Cross-subsystem ID resolution** — `intsToUuidsBatch` over
176
+ the final result set. Sub-budget: ≤ 0.1 ms per 1 K results.
177
+
178
+ The sub-budgets sum to within the top-level budget when the
179
+ query plan is correct. They blow the budget when the plan is
180
+ wrong (e.g. vector search runs over the whole corpus when a
181
+ 1 %-selective filter could prune it first). The query planner
182
+ described in *Investments* below is what keeps the plan correct.
183
+
184
+ ## The cor 3.0 performance investments
185
+
186
+ The shadow-page rework (G4.1, G4.4, G4.2 redo per the snapshot
187
+ safety contract) sets the substrate. The following investments
188
+ build on it to reach the budget at billion scale:
189
+
190
+ 1. **Cross-subsystem query planner.** Today `find()` runs filter
191
+ → vector → graph in a fixed order. The planner uses
192
+ `metadataIndex.getCountForCriteria` for cardinality estimates
193
+ and picks the lowest-cost order per query (e.g. a 0.01 %-
194
+ selective filter goes first, prunes 99.99 % of vector work).
195
+ PostgreSQL does this. Pure vector DBs don't.
196
+
197
+ 2. **Batched cross-subsystem APIs.** Every napi boundary call
198
+ pays ~5 μs of overhead. Resolving 10 K UUIDs one-at-a-time
199
+ costs 50 ms in napi alone, before any real work. Batched
200
+ variants (`intsToUuidsBatch`, `getNeighborsBatch`,
201
+ `getVerbsBatchCached`) amortise that to ~0.5 μs per item.
202
+ The hot path uses batches exclusively.
203
+
204
+ 3. **Lock-free delta cache reads.** The LSM delta cache (added
205
+ in G4.3, applied in G4.1 + G4.4) currently sits behind a
206
+ `Mutex`. A sharded `DashMap` or `ArcSwap<Arc<HashMap>>` MVCC
207
+ pattern lifts the read-side lock entirely, lets concurrent
208
+ `find()` calls scale linearly with cores.
209
+
210
+ 4. **SIMD bitmap operations.** CRoaring uses portable
211
+ intersect/union; modern AVX-512 popcnt and `vpternlogq`
212
+ kernels are ~3–5× faster on bitmap-heavy filter combinations.
213
+
214
+ 5. **DiskANN predicate pushdown.** The Vamana beam search
215
+ currently scores every neighbor of every visited node. With a
216
+ metadata-bitmap predicate passed into the search engine,
217
+ pruned nodes are skipped during the walk, not after. Massive
218
+ win for high-selectivity hybrid queries.
219
+
220
+ 6. **Pipelined result resolution.** DiskANN currently returns all
221
+ top-K candidates before resolution starts. Pipelining the
222
+ resolution (`intsToUuidsBatch` over the first 10 candidates
223
+ while the walk continues) hides the resolution latency
224
+ entirely.
225
+
226
+ 7. **Zero-allocation hot path.** `find()` allocates intermediate
227
+ `Vec<u64>` and `HashSet<String>` per query. A bumpalo-style
228
+ arena allocator (reset per query, no per-element free)
229
+ removes the allocation pressure and the GC pause.
230
+
231
+ Each investment ships with the gates above as the proof point.
232
+
233
+ ## Forbidden patterns
234
+
235
+ The performance contract has anti-patterns the way the snapshot
236
+ safety contract does:
237
+
238
+ 1. **Per-row napi calls on the hot path.** Anything in `find()`
239
+ that does `for (id of ids) { resolve(id) }` over napi is
240
+ incorrect. Use a batch.
241
+
242
+ 2. **Lock acquisition on every read.** A `Mutex` on the read
243
+ path costs at minimum a CAS on every call. At 100 K QPS with
244
+ contention, this is a bottleneck before the first real
245
+ operation runs. Use `ArcSwap` or per-shard locking.
246
+
247
+ 3. **Synchronous compactions on the hot write path.** Compaction
248
+ is a background operation. A write that blocks for the
249
+ duration of a compaction is a p99 cliff; we currently run
250
+ compaction synchronously in G4.3's `ShadowPageEndpointsStore`
251
+ (acceptable at 3.0 launch scale) but the pattern is
252
+ explicitly recognised as a future fix.
253
+
254
+ 4. **String allocation in the inner loop.** UUIDs are converted
255
+ to strings for the JS boundary, but the conversion has to
256
+ happen once per result row, not per intermediate set. Carry
257
+ integer ids through the planner; convert at the egress.
258
+
259
+ 5. **Quadratic scans where indexed lookups exist.** Filter
260
+ evaluation that walks all entities checking each against a
261
+ predicate, instead of consulting the metadata index, is the
262
+ hidden cliff at scale. Every `where` clause is index-
263
+ accelerated or it's a release blocker.
264
+
265
+ ## See also
266
+
267
+ - `docs/snapshot-safety.md` — the snapshot safety contract this
268
+ performance work is co-designed with.
269
+ - `docs/verification-report.md` — measured baseline numbers
270
+ (SIFT1M / SIFT10M / 1M synthetic, AVX-512 baseline) that the
271
+ budget is anchored on.
272
+ - `docs/comparison.md` — comparison of brainy + cor against
273
+ pure vector / pure graph / pure document databases on the
274
+ triple-intelligence query.
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: Cor Performance
3
+ slug: cor/performance
4
+ public: true
5
+ category: cor
6
+ template: guide
7
+ order: 1
8
+ description: Measured benchmarks for SIMD distance calculations, vector quantization, aggregation, and column-store orderBy sort, plus the cross-language consistency guarantee. Run yourself with npm run bench.
9
+ next:
10
+ - cor/comparison
11
+ - cor/aggregation
12
+ ---
13
+
14
+ # Cor Performance
15
+
16
+ All benchmarks measured using `vitest bench` on the included benchmark suite. Run yourself with:
17
+
18
+ ```bash
19
+ npm run bench
20
+ ```
21
+
22
+ ## Distance Calculations
23
+
24
+ 384-dimensional vectors (all-MiniLM-L6-v2 embedding size).
25
+
26
+ | Operation | Throughput | Mean Latency | P99 |
27
+ |-----------|-----------|-------------|-----|
28
+ | `cosineDistance` (single pair) | 45,965 ops/s | 21.8 μs | 34.1 μs |
29
+ | `euclideanDistance` (single pair) | 46,320 ops/s | 21.6 μs | — |
30
+ | `cosineDistanceSq8` (quantized pair) | 3,715,855 ops/s | 0.27 μs | 0.3 μs |
31
+ | `cosineDistanceSq8Batch` (1K vectors) | 2,142 ops/s | 467 μs | 1.1 ms |
32
+ | `cosineDistanceBatch` (1K vectors) | 77 ops/s | 13.0 ms | — |
33
+
34
+ **Key insight**: SQ8 quantized distance is **81x faster** than full-precision for single-pair comparisons. For batch operations, SQ8 batch is **28x faster** than full-precision batch.
35
+
36
+ ### Quantization
37
+
38
+ | Operation | Throughput | Mean Latency |
39
+ |-----------|-----------|-------------|
40
+ | `quantizeSq8` (384-dim) | 74,020 ops/s | 13.5 μs |
41
+ | `dequantizeSq8` (384-dim) | 53,440 ops/s | 18.7 μs |
42
+
43
+ ## Aggregation Engine
44
+
45
+ | Operation | Throughput | Mean Latency |
46
+ |-----------|-----------|-------------|
47
+ | `incrementalUpdate` (1K entities, 3 metrics) | 809 ops/s | 1.2 ms |
48
+ | `rebuildAggregate` (10K entities, 5 groups) | 475 ops/s | 2.1 ms |
49
+ | `rebuildAggregate` (100K entities, Rayon) | 66 ops/s | 15.2 ms |
50
+ | `queryAggregate` (1K groups, sort + paginate) | 986 ops/s | 1.0 ms |
51
+ | `computeGroupKey` (10K entities, time bucketing) | 146 ops/s | 6.8 ms |
52
+
53
+ Rebuild scales linearly with entity count. Rayon parallelism activates above 1,000 entities.
54
+
55
+ ## Column Store — orderBy Sort
56
+
57
+ The native column store backs Brainy's `find({ orderBy, limit })`. Its `sortTopK`
58
+ (O(N log K) k-way merge over sorted segments) scales near-linearly with entity
59
+ count.
60
+
61
+ | Operation | Entities | Mean Latency |
62
+ |-----------|---------:|-------------|
63
+ | `sortTopK` (k=100) | 1,000 | ~0.028 ms |
64
+ | `sortTopK` (k=100) | 10,000 | ~0.21 ms |
65
+ | `sortTopK` (k=100) | 100,000 | ~3.3 ms |
66
+
67
+ MEASURED on AMD Ryzen 9 7950X3D, Node 22, release native (`src/benchmarks/scaling.bench.test.ts`).
68
+ Growth from 1k→100k is ~125x against a 100x linear ideal — emphatically not the
69
+ ~10,000x of an O(N²) sort. `scaling.bench.test.ts` runs in CI and fails the build
70
+ if the growth ratio ever turns super-linear.
71
+
72
+ ## Cross-Language Consistency
73
+
74
+ Cor is a pure accelerator: every native result is byte-for-byte equal to the
75
+ Brainy JavaScript baseline it replaces, so query results are identical with or
76
+ without the plugin installed. This is enforced by a 104-test cross-language parity
77
+ suite (`src/native/crossLanguageParity.test.ts`) covering tokenization, value
78
+ normalization (the UTF-16/ASCII boundary), code-point string collation, SQ8
79
+ quantized distance, top-K ranking, and roaring/msgpack round-trips against
80
+ Brainy's golden outputs.
81
+
82
+ ## Serialization
83
+
84
+ | Operation | Throughput | Mean Latency |
85
+ |-----------|-----------|-------------|
86
+ | `msgpackEncode` (1K entities) | 235 ops/s | 4.3 ms |
87
+ | `msgpackDecode` (1K entities) | 355 ops/s | 2.8 ms |
88
+ | `msgpackEncodeBatch` (1K entities) | 290 ops/s | 3.4 ms |
89
+ | `msgpackDecodeBatch` (1K entities) | 356 ops/s | 2.8 ms |
90
+ | `RoaringBitmap32.create` (10K elements) | 3,067 ops/s | 326 μs |
91
+ | `RoaringBitmap32.serialize` (10K elements) | 361,258 ops/s | 2.8 μs |
92
+ | `RoaringBitmap32.deserialize` (10K elements) | 548,714 ops/s | 1.8 μs |
93
+ | `RoaringBitmap32.and` (two 10K bitmaps) | 1,379 ops/s | 725 μs |
94
+ | `RoaringBitmap32.or` (two 10K bitmaps) | 1,219 ops/s | 820 μs |
95
+ | `encodeConnections` (1K lists) | 913 ops/s | 1.1 ms |
96
+ | `decodeConnections` (1K lists) | 1,078 ops/s | 928 μs |
97
+
98
+ ## Hardware Recommendations
99
+
100
+ - **CPU**: Multi-core for Rayon parallel rebuild. x86_64 for SIMD distance.
101
+ - **Memory**: Cor native allocations are tracked and reported via the cache subsystem.
102
+ - **Storage**: SSD (NVMe ideal) for the memory-mapped HNSW index and graph-adjacency SSTables — the kernel keeps the hot working set in RAM and pages the rest from disk. See [Scaling & Resource Management](./scaling.md).
103
+
104
+ ## Running Benchmarks
105
+
106
+ ```bash
107
+ # Run all benchmarks
108
+ npm run bench
109
+
110
+ # Run specific benchmark
111
+ npx vitest bench src/benchmarks/distance.bench.ts
112
+
113
+ # Run with verbose output
114
+ npx vitest bench --reporter=verbose
115
+ ```
116
+
117
+ Benchmarks use vitest bench mode and run multiple iterations to produce stable statistics. Results include Hz (ops/sec), min, max, mean, P75, P99, P99.5, P99.9, and relative margin of error (RME).