mirror of
https://github.com/qdrant/qdrant.git
synced 2026-09-25 07:27:41 -05:00
* TurboQuantizer::score_precomputed_batch: score a contiguous run of vectors Batch counterpart of `score_precomputed` for vectors stored back to back at `quantized_size()`: the width's kernel scores the whole run of codes in one `dotprod_batch` call, then a second pass applies each vector's extras. L1 dequantizes per vector and stays a plain loop. Tested against per-vector `score_precomputed` for every width, distance, and mode over run lengths that leave every group remainder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * EncodedStorage::for_each_run: serve consecutive offsets as contiguous runs `for_each_run(offsets, callback(first, count, bytes))` splits the offsets into maximal runs of consecutive ids the storage can serve from one contiguous slice, so a sequential scan resolves chunk lookups and reads once per run instead of once per vector. The default serves every vector as its own run; `for_each_consecutive_run` is the shared run detection for storages that override it, with a per-run cap for chunk boundaries. The test storage overrides it (its data is one flat buffer). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * EncodedVectors::score_points: batched scoring entry point, run-batched for TQ `score_points(query, offsets, scores)` scores a batch of points. The default keeps the per-vector loop the scorers run today, so SQ/PQ/BQ are unchanged. TurboQuant overrides it: on RAM/mmap storages it walks `for_each_run` and scores each contiguous run with one `score_precomputed_batch` call, hoisting the score inversion out of the loop; backends with async reads keep the pipelined per-vector path. Non-consecutive offsets degrade to single-vector runs, so scattered access keeps its previous cost. Integration test: `score_points` vs `score_point` for every bit width and mode, Dot and inverted L2, over sequential, scattered and descending id orders. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Quantized storages: for_each_run over their contiguous regions The RAM storage and both chunked mmap storages cap runs at their chunk boundary and serve each run with one `get_many`; the single-file mmap storage serves any run as one sequential read. Unit test on the RAM storage: runs cover every offset once, in order, with bytes identical to per-point reads, across the internal chunk boundary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * QuantizedQueryScorer: score batches through EncodedVectors::score_points Routes `score_stored_batch` through the batched entry point, so TurboQuant-as-quantization scans score contiguous runs with one kernel call per run; SQ/PQ/BQ keep the per-vector loop via the default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * TurboScoring::score_query_batch: run-batched scoring for Turbo4 storages Adds the batch counterpart of `score_query_bytes` to the trait, with one shared implementation over the storage's `EncodedStorage`: consecutive ids are coalesced into contiguous runs, each run scored by a single `score_precomputed_batch` call, and the metric sign applied once over the batch. Backends with async reads keep the pipelined per-vector path. `TurboQueryScorer::score_stored_batch` now calls it. The batch-vs-single storage test grows to 8192 vectors so a full ascending scan crosses a chunk boundary of the chunked backend, and runs that scan on the chunked, mmap and io_uring backends. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * score_precomputed_batch: keep the extras pass in L1 The kernel pass and the extras pass now alternate over sub-runs of 64 vectors instead of each covering the whole run: for a run of several hundred vectors the second pass otherwise refetched every vector's extras from L2. Measured with 512-vector runs from the full-scan driver at dim 512: the regression against 64-vector runs went from +11 % to +2 %. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Bench: exhaustive search over Turbo4 storages through the plain-index driver `turbo4_full_scan` runs `BatchFilteredSearcher::peek_top_visible` — the exact path of a non-indexed search — over 200k normalized random vectors for Turbo4 as datatype (appendable chunked, in RAM) and Turbo4 as quantization (over a RAM dense storage), at dims 64 to 1024, so the fixed per-point cost of the scan driver is measured next to the kernel. `TURBO_SCAN_DIMS=64,128` narrows the dims while iterating. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>