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
Round-3 perf push sub-issue (tracked under umbrella #535).
[M] Stop persisting the HNSW graph as redundant manual u64s; pack as CSR + variable-byte deltas
Where: laurus/src/vector/index/hnsw/writer.rs:1259-1297
(write path) and :417-485 (read path).
Current behaviour: graph dump is node_count u64 followed by
per-node [doc_id u64, layer_count u32, per layer [neighbour_count u32, neighbours u64*]]. Every edge takes 8 bytes even though
neighbour IDs are local to the segment and almost always <2^32. No
compression at all.
Why it might be a bottleneck: I/O size is doubled vs Lucene's
packed graph; cold-start mmap loads more pages than necessary. For a
10 M-node segment with avg ~24 neighbours per node, the graph dump is
~2 GB at u64.
Reference precedent: Lucene99HnswVectorsFormat writes layer-0 as [delta(n) varint]* per node, sorted neighbours, plus a sparse upper
layer dump. Qdrant uses bincode with u32 IDs over a length-prefixed
layout.
Proposed layout (LVS2 graph section):
per layer:
offsets : Vec<u64> length n+1 // mmap-friendly random access
edges : Vec<u32> // (sorted within node, delta-varint
// optional later)
Suggested direction: convert IDs to u32 (already proposed for
CSR); store sorted-within-node neighbours so a future v3 can introduce
delta-varint codes without breaking compatibility; align offsets
array to 8 B for direct mmap. Combine with Add parallel search #1 and Add parallel index #2.
Risk / scope: format break; needs LVS2 magic; pairs naturally
with the in-memory CSR change.
ID: VI-09 — see ~/.claude/tasks/laurus/20260523_perf_round3_audit/task_list.md for the full Round-3 issue list.
Round-3 perf push sub-issue (tracked under umbrella #535).
[M] Stop persisting the HNSW graph as redundant manual u64s; pack as CSR + variable-byte deltas
Where:
laurus/src/vector/index/hnsw/writer.rs:1259-1297(write path) and
:417-485(read path).Current behaviour: graph dump is
node_count u64followed byper-node
[doc_id u64, layer_count u32, per layer [neighbour_count u32, neighbours u64*]]. Every edge takes 8 bytes even thoughneighbour IDs are local to the segment and almost always <2^32. No
compression at all.
Why it might be a bottleneck: I/O size is doubled vs Lucene's
packed graph; cold-start mmap loads more pages than necessary. For a
10 M-node segment with avg ~24 neighbours per node, the graph dump is
~2 GB at u64.
Reference precedent: Lucene99HnswVectorsFormat writes layer-0 as
[delta(n) varint]*per node, sorted neighbours, plus a sparse upperlayer dump. Qdrant uses
bincodewithu32IDs over a length-prefixedlayout.
Proposed layout (LVS2 graph section):
Suggested direction: convert IDs to
u32(already proposed forCSR); store sorted-within-node neighbours so a future v3 can introduce
delta-varint codes without breaking compatibility; align
offsetsarray to 8 B for direct mmap. Combine with Add parallel search #1 and Add parallel index #2.
Risk / scope: format break; needs LVS2 magic; pairs naturally
with the in-memory CSR change.
ID:
VI-09— see~/.claude/tasks/laurus/20260523_perf_round3_audit/task_list.mdfor the full Round-3 issue list.