Summary
Late-interaction retrieval (LodeLateInteractionIndex, #25) stores each document's patch matrix as base64 in the per-row text sidecar. The resident exact scan decodes every document into one in-memory float32/float16 matrix on build; corpora over resident_max_bytes fall back to a streaming path that re-reads and base64-decodes documents from disk on every query. Both the build cost and the streaming cost are dominated by base64 decode and full materialization.
A raw-binary, memory-mapped patch store would remove both: store the patch matrices as a contiguous binary file (plus a small offset/dtype index) and mmap it, so:
- the resident matrix is the mmap itself (or a cheap view), with no base64 decode on build;
- corpora past the RAM budget are served from the OS page cache via the mmap instead of re-decoding per query, so the over-budget path is fast (page-cache bound) rather than disk-decode bound;
- on-disk size drops by the ~33% base64 overhead.
Sketch
- A binary sidecar (e.g.
patches.libin) holding all patch matrices concatenated at the index's storage precision (float32 / float16 / int8 + per-vector scales), with a parallel offset/count index keyed by document id. Keep it outside the engine's *.json scan (as the existing .meta config does).
- Build the resident matrix as an mmap view; for int8/float16 keep the GEMM upcast chunked as today.
- Maintain it incrementally alongside the existing resident delta: append new documents to the binary file, tombstone removed ones, compact periodically (mirrors the in-memory delta/compaction already in place).
- Keep the current base64-sidecar path as a fallback for read-only handles or where mmap is unavailable.
Out of scope
- Changing the MaxSim kernel or the engine's vector storage.
- The candidate-pruning / ANN direction for very large corpora (separate concern).
Follow-up to #25.
Summary
Late-interaction retrieval (
LodeLateInteractionIndex, #25) stores each document's patch matrix as base64 in the per-row text sidecar. The resident exact scan decodes every document into one in-memory float32/float16 matrix on build; corpora overresident_max_bytesfall back to a streaming path that re-reads and base64-decodes documents from disk on every query. Both the build cost and the streaming cost are dominated by base64 decode and full materialization.A raw-binary, memory-mapped patch store would remove both: store the patch matrices as a contiguous binary file (plus a small offset/dtype index) and
mmapit, so:Sketch
patches.libin) holding all patch matrices concatenated at the index's storage precision (float32 / float16 / int8 + per-vector scales), with a parallel offset/count index keyed by document id. Keep it outside the engine's*.jsonscan (as the existing.metaconfig does).Out of scope
Follow-up to #25.