Skip to content

perf(vector/index): tighten on-disk record by promoting field_name to a per-segment dictionary #633

Description

@mosuka

Round-3 perf push sub-issue (tracked under umbrella #535).

[M] Tighten on-disk record by promoting field_name to a per-segment dictionary

  • Where: laurus/src/vector/index/quantized_io.rs:14-23,
    :74-85; HNSW write loop (hnsw/writer.rs:1210-1218); Flat / IVF
    equivalents.

  • Current behaviour: every vector record stores its field_name
    inline (u32 len + UTF-8 bytes). Most segments have one field; this
    is pure overhead.

  • Why it might be a bottleneck: For 10 M vectors with a 32-byte
    field name ("text_embedding_e5_large_v2_768d"), that's 320 MB of
    duplicated UTF-8 alongside the int8 payload.

  • Reference precedent: Lucene stores field names in a per-segment
    field-info dictionary (fnm) and references them by ordinal in every
    vector record.

  • Current layout:

    per record: [doc_id u64][name_len u32][field_name UTF-8][int8 dim][meta 8B]
    
  • Proposed layout:

    segment header: field_dict_len u16, field_dict (u16 len + UTF-8)*
    per record:    [doc_id u64][field_id u16][int8 dim][meta 8B]
    ─ field_id indexes into the dictionary above ─
    

    For the single-field case field_id can be elided entirely (encode
    as field_dict_len = 0 → implicit field_id = 0).

  • Suggested direction: extend VectorSegmentHeader (or move to
    LVS2) to carry the field dictionary; readers translate field_id back
    to the string on first access (cached in a SmallVec<String> per
    segment).

  • Risk / scope: format break (must bump LVS version); pairs with
    the columnar restructure (Add parallel index #2).



ID: VI-15 — see ~/.claude/tasks/laurus/20260523_perf_round3_audit/task_list.md for the full Round-3 issue list.

Task list (2026-07-12 start)

Phase 0: Investigation

  • In-memory field-name lifecycle map (vector_ids, per-field caches, pool map keys, OnDemand offset keys, iterators, hit/result types, per-call String allocations; merge / NRT-active / writer buffers)
  • Design constraints (version-gate matrix on top of perf(vector/hnsw): segment-local u32 ordinal graph + LVS1 v2 format (sub-issue of #686) #853's v2; empty segments; legacy multi-field; WAL/manifest/bindings impact; dictionary placement)
  • Opportunity ceiling (on-disk bytes + load time + RAM, with fire-condition line evidence)
  • Post investigation summary as an issue comment

Phase 1: Plan

  • Scope decision: A+B full, staged as three sub-issue PRs → plan posted as an issue comment

Phase 2+: Implementation (sub-issues)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions