microsoft / microsoft/DiskANN

NeighborPriorityQueue::insert() panics on aarch64 (emulated SIMD)

Open
#784 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
1.9k
Forks
454
Avg merge
3d 22h
Merged PRs (30d)
35

Description

Bug

NeighborPriorityQueue::insert() panics during index build on aarch64 (Apple Silicon M3 Pro):

thread '<unnamed>' panicked at diskann-0.45.0/src/neighbor/queue.rs:156:26:
insertion index (is 129) should be <= len (is 128)

Versions: diskann = "0.45.0", also verified 0.46.0 has identical queue.rs.

Reproduction

  • Platform: macOS 15.4, Apple M3 Pro (aarch64)
  • Dataset: 563 f32 vectors, 384 dimensions (MiniLM embeddings)
  • Config: max_degree=64, MaxDegree::default_slack() (1.3), build_complexity=128, alpha=1.2, metric=L2
  • Happens during DiskANNIndex::insert() after inserting ~100-200 vectors (non-deterministic)

Minimal caller code:

let prune_kind = PruneKind::from_metric(DistanceMetric::L2);
let mut builder = Builder::new(64, MaxDegree::default_slack(), 128, prune_kind);
builder.alpha(1.2);
let config = builder.build()?;
let index = DiskANNIndex::new(config, provider, None);

// Insert 563 vectors sequentially
for (label, vector) in vectors {
    runtime::block_on(index.insert(FullPrecisionStrategy::new(), &DefaultContext, &label, &vector))?;
}

Analysis

In queue.rs:133-167, the insert() method for a fixed-size queue:

  1. Guard (line 140): self.get_unchecked(self.size - 1) < nbr → returns false (last element ≥ nbr by distance)
  2. Lower bound (line 145): get_lower_bound(&nbr) → returns self.size (129), meaning "all elements compare less-than via SIMD"
  3. Truncate (line 150-153): removes last element, self.size becomes 128, vec length becomes 128
  4. Insert (line 156): self.id_visiteds.insert(129, ...)panics because 129 > vec.len() (128)

Steps 1 and 2 are contradictory: the guard says last element has distance >= nbr.distance, but get_lower_bound says all distances are < nbr.distance. On aarch64 there's no native diskann-wide SIMD backend — it falls back to emulated.rs which does scalar f32 >= f32 comparisons. I confirmed the vectors contain no NaN/Inf values, so the comparisons should agree.

I haven't been able to pinpoint exactly why the guard and get_lower_bound disagree. It may be related to the queue capacity (129 = build_complexity + 1 frozen point) and how load_simd_first handles the tail.

Workaround

Clamping insert_idx after truncation fixes the panic and preserves sort order:

if self.size == self.capacity {
    self.id_visiteds.truncate(self.size - 1);
    self.distances.truncate(self.size - 1);
    self.size -= 1;
}

let insert_idx = insert_idx.min(self.size);  // <-- fix

self.id_visiteds.insert(insert_idx, (nbr.id, false));
self.distances.insert(insert_idx, nbr.distance);

This is safe because if insert_idx == old_size, the element has the largest distance and should go at the end (replacing the truncated element).

Question

Am I configuring something wrong, or is this a bug in the queue logic? Happy to provide more details or a standalone repro.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in queue.rs:133-167, especially the fixed-size insert path, and compare its guard and get_lower_bound behavior with the emulated SIMD implementation in emulated.rs. Run the supplied aarch64 reproduction using the stated queue configuration; done means insertion no longer panics and the queue remains sorted when the insertion index reaches the truncated length.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
search
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.