perf(agent): unranked BM25 scores and a top-k merge — same rankings, 4-5x faster
Fusion min-max normalises over every keyword match, so hybrid_search asked BM25 for a ranked list of the whole corpus: a hash insert per posting, then a sort of every match, then the merge sorted every candidate again to keep k. - BM25Index::scores returns every match unsorted, accumulated in a dense array (contributions are strictly positive, so zero means untouched). search() is built on it with the bounded heap. - merge_vector_keyword partitions out its top k (select_nth) and orders only those, with the same score-then-id order. - Both hybrid paths use scores(). Rankings are identical (equivalence tests for both changes). p50 0.24 -> 0.07 ms (1K), 2.1 -> 0.49 ms (10K), 23 -> 4.65 ms (100K). The harness gains --fusion-study, which measured the alternative — capping the keyword pool — and found it changes the top-10 for most queries (overlap 0.83-0.92, different #1 for 10-35%) for only a 2x saving. Not adopted. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
f15bf2eb22
commit
390a2e3836
@@ -221,6 +221,28 @@ build: 21084.6 ms (4743 vectors/s) · exact scan: 39 QPS, p50 24739 µs
|
||||
| 256 | 0.9990 | 3731 | 254 | 697 |
|
||||
|
||||
|
||||
### After: unranked keyword scores, top-k merge (rankings unchanged)
|
||||
|
||||
A fusion study (`search_harness --fusion-study`) showed that capping the
|
||||
keyword candidate pool is **not** a safe optimisation: against the current
|
||||
full-corpus normalisation the final top-10 overlap is only 0.83-0.92 and the
|
||||
first result changes for 10-35% of queries, for only a 2x saving. So the fusion
|
||||
semantics were left alone and the same answer made cheaper: fusion needs every
|
||||
keyword score but not their ranking, so BM25 now returns them unsorted from a
|
||||
dense accumulator (it hashed every posting and then sorted every match), and
|
||||
the merge selects its top k instead of sorting every candidate. Steady-state
|
||||
p50: **0.24 -> 0.07 ms** (1K), **2.1 -> 0.49 ms** (10K), **23 -> 4.65 ms**
|
||||
(100K) — **79x / 100x / 190x** faster than the v2.3.0 baseline, with identical
|
||||
results.
|
||||
|
||||
### End to end: `HDF5Memory::hybrid_search` (k = 10, weights 0.7 / 0.3)
|
||||
|
||||
| N | ingest ms | cold index build ms | checkpoint ms | open ms | first query after open ms | p50 ms | p99 ms | QPS |
|
||||
|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
||||
| 1000 | 11 | 112 | 4.0 | 1.1 | 1.4 | 0.07 | 0.08 | 14077.9 |
|
||||
| 10000 | 104 | 1487 | 33.8 | 13.7 | 13.9 | 0.49 | 0.51 | 2020.9 |
|
||||
| 100000 | 1376 | 20285 | 728.9 | 353.1 | 142.2 | 4.65 | 4.78 | 214.7 |
|
||||
|
||||
## Vector Search Latency
|
||||
|
||||
Brute-force cosine similarity over 384-dimensional embeddings (OpenAI text-embedding-3-small size).
|
||||
|
||||
Reference in New Issue
Block a user