Round-3 perf push sub-issue (tracked under umbrella #536).
[S] NaN handling in float heaps via unwrap_or(Ordering::Equal) — silent reorder risk
- Where:
hnsw/searcher.rs:270-310, vector/store.rs:480-495, engine.rs:1485. All
use partial_cmp(...).unwrap_or(Ordering::Equal).
- Current behavior: A NaN distance gets treated as equal to everything → heap invariant
silently broken → possible wrong top-K. Kernel emits NaN-free values today but a single
bad input via fallback path would corrupt the heap.
- Reference precedent:
ordered_float::NotNan; Lucene asserts non-NaN; Qdrant uses
f32::total_cmp.
- Suggested direction: Use
f32::total_cmp (Rust 1.62+) for deterministic order, or
validate at kernel boundary. Wrap heap items in NotNanF32 newtype. With the packed
candidate (above), this is automatic.
- Risk / scope: Small.
ID: VS-24 — 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 #536).
[S] NaN handling in float heaps via
unwrap_or(Ordering::Equal)— silent reorder riskhnsw/searcher.rs:270-310,vector/store.rs:480-495,engine.rs:1485. Alluse
partial_cmp(...).unwrap_or(Ordering::Equal).silently broken → possible wrong top-K. Kernel emits NaN-free values today but a single
bad input via fallback path would corrupt the heap.
ordered_float::NotNan; Lucene asserts non-NaN; Qdrant usesf32::total_cmp.f32::total_cmp(Rust 1.62+) for deterministic order, orvalidate at kernel boundary. Wrap heap items in
NotNanF32newtype. With the packedcandidate (above), this is automatic.
ID:
VS-24— see~/.claude/tasks/laurus/20260523_perf_round3_audit/task_list.mdfor the full Round-3 issue list.