Skip to content

perf: accelerate fp16 HNSW search with AMX#7933

Open
xtangxtang wants to merge 1 commit into
lance-format:mainfrom
epeshared:feat/amx-fp16-hnsw
Open

perf: accelerate fp16 HNSW search with AMX#7933
xtangxtang wants to merge 1 commit into
lance-format:mainfrom
epeshared:feat/amx-fp16-hnsw

Conversation

@xtangxtang

@xtangxtang xtangxtang commented Jul 23, 2026

Copy link
Copy Markdown

Summary

  • Add a 16-wide DistCalculator::distance_batch_16 hook with a scalar-compatible default implementation.
  • Route flat FP16 + Dot HNSW distance batches through a new AMX-FP16 (_tile_dpfp16ps) kernel.
  • Add runtime CPUID/XTILEDATA permission checks, compiler probing, safe fallback behavior, and reusable per-thread gather storage.
  • Add FP16 HNSW recall and batch-distance coverage, including AMX-disabled fallback coverage.

The batched graph loop preserves visited-node ordering and applies neighbor acceptance in the same order as the scalar path. Non-FP16-Dot backends retain the scalar-compatible default.

Validation

  • cargo fmt --all -- --check
  • cargo clippy -p lance-linalg --features fp16kernels --tests
  • cargo clippy -p lance-index --features lance-linalg/fp16kernels --tests
  • cargo test -p lance-linalg --features fp16kernels dot_f16 -- --nocapture
  • cargo test -p lance-index --features lance-linalg/fp16kernels f16 -- --nocapture
  • LANCE_DISABLE_AMX=1 cargo test -p lance-index --features lance-linalg/fp16kernels f16 -- --nocapture
  • LANCE_NGRAM_NUM_PARTITIONS=128 cargo test -p lance-index --lib

The FP16 HNSW test reports recall@10 of 0.993 on both AMX and fallback paths.

Performance

Measured on an Intel Xeon 6972P using flat FP16 + Dot HNSW search. The comparison below is AMX-FP16 enabled versus LANCE_DISABLE_AMX=1 using the same post-patch batched HNSW traversal and FP16 fallback path:

Dimension 1 thread 32 threads 384 threads
128 1.53x 1.52x 1.12x
768 1.86x 2.06x 1.50x
1536 2.37x 2.22x 1.90x

The gain grows with dimension as fixed tile/setup overhead is amortized, and narrows at full machine concurrency as memory bandwidth and scheduling become the bottleneck. These figures isolate the AMX kernel and dispatch benefit within the post-patch batched path; they are not a three-way pre-patch / post-patch-AMX-off / post-patch-AMX-on comparison. FP16 HNSW recall@10 remained 0.993 in both AMX and fallback runs.

@github-actions github-actions Bot added A-index Vector index, linalg, tokenizer performance labels Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an AMX-FP16 batched dot-product kernel, metric-aware distance batching, and 16-wide neighbor distance computation in graph search. Build detection, runtime dispatch, scalar fallbacks, numerical checks, recall tests, and benchmark coverage are included.

Changes

AMX-FP16 Batched Vector Search

Layer / File(s) Summary
AMX-FP16 kernel and build support
rust/lance-linalg/build.rs, rust/lance-linalg/src/simd/*
Build-time compiler probing, Linux permission handling, CPUID detection, Rust FFI bindings, and the C AMX-FP16 kernel are added.
Batched fp16 dot-distance API
rust/lance-linalg/src/distance.rs, rust/lance-linalg/src/distance/dot_f16.rs
The fp16 dot-product API validates candidate dimensions, selects AMX when available, and otherwise uses a scalar fallback with correctness tests.
Distance calculator batching
rust/lance-index/src/vector/storage.rs, rust/lance-index/src/vector/flat/storage.rs
DistCalculator gains a default 16-item batch method; flat fp16 dot distances use fused computation while other metrics retain scalar behavior.
Batched graph search integration
rust/lance-index/src/vector/graph.rs
Beam search batches full groups of 16 neighbors, shares acceptance logic with the scalar remainder path, and tests recall and batch execution.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: enhancement

Suggested reviewers: jackye1995, bubblecal

Sequence Diagram(s)

sequenceDiagram
  participant beam_search_loop
  participant DistCalculator
  participant FlatFloatDistanceCalc
  participant dot_f16_batch_16
  beam_search_loop->>DistCalculator: Request distances for 16 neighbor IDs
  DistCalculator->>FlatFloatDistanceCalc: Dispatch distance_batch_16
  FlatFloatDistanceCalc->>dot_f16_batch_16: Compute fp16 Dot batch
  dot_f16_batch_16-->>FlatFloatDistanceCalc: Return 16 distances
  FlatFloatDistanceCalc-->>beam_search_loop: Apply neighbor acceptance logic
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: AMX-accelerated FP16 HNSW search.
Description check ✅ Passed The description matches the changeset and accurately describes the AMX FP16 HNSW batching, fallback behavior, and validation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
rust/lance-index/src/vector/flat/storage.rs-413-413 (1)

413-413: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Drop the _ prefix now that _distance_type is read. The binary constructors now store the parameter into the distance_type field, so it is no longer an unused binding. Rename the parameter (and this assignment) to distance_type in both new_binary (Line 413) and new_binary_from_id (Line 431).

As per coding guidelines: "Reserve _-prefixed names for truly unused bindings; if a variable is read, remove the underscore."

♻️ Proposed rename
     fn new_binary(
         vectors: &'a FixedSizeListArray,
         query: ArrayRef,
-        _distance_type: DistanceType,
+        distance_type: DistanceType,
     ) -> Self {
         ...
-            distance_type: _distance_type,
+            distance_type,
             distance_fn: hamming,

Apply the same to new_binary_from_id (Line 431).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-index/src/vector/flat/storage.rs` at line 413, Rename the
distance-type parameter from _distance_type to distance_type, and update the
distance_type field assignment in both new_binary and new_binary_from_id;
preserve the existing constructor behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Other comments:
In `@rust/lance-index/src/vector/flat/storage.rs`:
- Line 413: Rename the distance-type parameter from _distance_type to
distance_type, and update the distance_type field assignment in both new_binary
and new_binary_from_id; preserve the existing constructor behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 3da444c0-c467-448a-9f36-17e0ab854fd4

📥 Commits

Reviewing files that changed from the base of the PR and between 8f51a21 and 8365d85.

📒 Files selected for processing (9)
  • rust/lance-index/src/vector/flat/storage.rs
  • rust/lance-index/src/vector/graph.rs
  • rust/lance-index/src/vector/storage.rs
  • rust/lance-linalg/build.rs
  • rust/lance-linalg/src/distance.rs
  • rust/lance-linalg/src/distance/dot_f16.rs
  • rust/lance-linalg/src/simd.rs
  • rust/lance-linalg/src/simd/amx_fp16.c
  • rust/lance-linalg/src/simd/amx_fp16.rs

@leohoare

Copy link
Copy Markdown
Contributor

I'm not a maintainer this is just my thoughts...

Relatively clean change but not sure if this should be the direction of an OSS package.

Narrow gain for significant change that's a hot loop for every user:

  • TDPFP16PS is the fp16 flavour of AMX, which is Granite Rapids and newer only. Most current AMX chips (Sapphire, Emerald Rapids) do not have it.
  • It only fires for flat fp16 storage plus the Dot metric. That is not the common config. Real HNSW deployments are almost always quantized (PQ or SQ).

Broad change:

  • change restructures the beam-search traversal to evaluate neighbors 16 at a time. Should this be tested separately without the AMX? Do we know if the speedup is here (not AMX) and merited.
  • New C FFI kernel in build.rs compiler probing, XTILEDATA permission handling, a SIGILL surface, per-thread gather buffers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-index Vector index, linalg, tokenizer performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants