Skip to content

refactor: unify runtime SIMD kernel dispatch - #105

Merged
gouyt13 merged 1 commit into
mainfrom
refactor/unified-kernel-dispatch
Sep 16, 2026
Merged

gouyt13 merged 1 commit into
mainfrom
refactor/unified-kernel-dispatch

Conversation

@gouyt13

@gouyt13 gouyt13 commented Sep 16, 2026

Copy link
Copy Markdown
Member

Summary

This PR centralizes runtime SIMD dispatch across the library, isolates ISA-specific implementations into dedicated translation units, and lets portable builds use optimized kernels without globally enabling -march=native.

It covers distance estimation, matrix operations, rotation, quantization, FastScan, IVF, HNSW, PiPNN, and SymphonyQG. It also converts SymphonyQG into a concrete float-only class and moves its implementation out of the public header.

Python APIs and persisted index formats remain unchanged.

Main changes

Centralized SIMD dispatch

  • Resolve generic, AVX2, and AVX-512 function pointers once during static initialization.
  • Route public and generic code through centralized dispatch entry points.
  • Add dispatch interfaces for:
    • Batch distance estimation
    • Matrix products, norms, and pairwise distances
    • HNSW search
    • FHT/Kac rotation
    • Quantization and FastScan helpers
    • IVF candidate insertion
  • Add generic implementations needed by portable builds.
  • Keep AVX2 and AVX-512 code in separately compiled translation units.
  • Isolate Eigen-backed matrix implementations per ISA to avoid target and ODR leakage.
  • Rename space_float.cpp to space_generic.cpp.

SymphonyQG

  • Replace QuantizedGraph<float> and QuantizedQuery<float> with concrete float-only classes.
  • Keep declarations in qg.hpp and move implementations to src/index/qg.cpp.
  • Update the builder, Python bindings, examples, documentation, and tests.
  • Preserve the Python API and existing raw, 4-bit, and 8-bit index formats.
  • Use the same neighbor traversal implementation in portable and native builds.
  • Combine batch estimation and candidate-mask generation.
  • Dispatch the combined operation to AVX2 or the generic fallback.
  • Preserve candidate and tie ordering, including the original unordered/NaN comparison semantics.

C++ callers must replace QuantizedGraph<float> with QuantizedGraph.

IVF, HNSW, and PiPNN

  • Move IVF batch insertion into a compiled dispatch boundary.
  • Route HNSW search through the shared runtime resolver.
  • Route PiPNN matrix operations through dispatched matrix kernels.
  • Keep centroid routing on the same runtime-selected distance kernels as flat IVF.
  • Preserve search parameters, result ordering, and quality.

Testing and tooling

  • Add backend-independent reference tests for generic, AVX2, and AVX-512 implementations where applicable.
  • Cover unaligned inputs and dimensions around SIMD-width and padding boundaries.
  • Add matrix, rotation, estimator, candidate-mask, FastScan, IVF, and initializer regression coverage.
  • Extend include-cleaner and clang-tidy configuration for the new source boundaries.
  • Document dispatch conventions and backend coverage in CONTRIBUTING.md and AGENTS.md.

Performance

Benchmarks used a Xeon Gold 6418H, GCC 14.3 Release builds, one thread pinned to CPU 2, and fixed saved indexes and queries.

IVF and HNSW portable/native gap

Workload Portable Native Gap
IVF 1-bit 9.414 ms 9.725 ms −3.20%
IVF 4-bit 25.227 ms 25.812 ms −2.26%
HNSW 4-bit 64.566 ms 64.149 ms +0.65%
IVF 20k clusters 142.214 ms 140.543 ms +1.19%

Negative values mean portable was faster.

PiPNN construction

Median construction time decreased from 0.460 s to 0.260 s, a 43.4% latency reduction and approximately 1.77× throughput improvement. Initial-graph nearest-neighbor coverage remained unchanged.

SymphonyQG

Workload: 8,192 vectors × 128 dimensions, 256 queries, L2, k=10, degree 32, shared raw/4-bit/8-bit indexes.

Mode ef Original portable Final portable Final native Final gap
Raw 100 7.484 ms 7.071 ms 7.157 ms −1.20%
Raw 400 33.702 ms 32.794 ms 33.255 ms −1.39%
4-bit 100 7.849 ms 7.297 ms 7.423 ms −1.70%
4-bit 400 35.230 ms 33.641 ms 33.846 ms −0.61%
8-bit 100 7.611 ms 7.215 ms 7.339 ms −1.70%
8-bit 400 34.259 ms 33.452 ms 33.674 ms −0.66%

Portable latency improved by approximately 2.4–7.0% versus the original portable baseline. The unified AVX2 implementation changed native latency by between −1.6% and +3.7% relative to the previous compiler-vectorized native path.

Correctness

  • Neighbor IDs and recall remained identical across compared builds.
  • Raw SymphonyQG distances remained exact.
  • Maximum quantized SymphonyQG distance difference was 1.52587890625e-5, within the established 2e-5 tolerance.
  • IVF and HNSW workloads returned identical IDs.
  • Matrix backends matched double-precision references within the declared tolerance.
  • Existing raw, 4-bit, and 8-bit SymphonyQG indexes remain compatible.
  • No intentional speed–quality tradeoffs were introduced.

Verification

  • Portable C++ suite: 143 tests passed before the final AVX2-only dispatch cleanup.
  • ASan/UBSan/LSan suite: 142 tests passed.
  • Final portable wheel: 148 Python tests passed.
  • Final native wheel: 148 Python tests passed.
  • Source-distribution wheel: 148 Python tests passed.
  • Portable and native wheel builds completed successfully.
  • Formatting, Python checks, configured clang-tidy, include-cleaner, and scoped git diff --check passed.
  • Strict MkDocs build passed.
  • Binary inspection confirmed that ISA-specific objects do not introduce weak duplicate implementations.
  • Binary inspection confirmed that the final QG combined mask has AVX2 and generic backends only.

AVX-512 execution was verified on the benchmark host. AVX2 implementations were covered by capability-guarded direct backend tests; separate AVX2-only hardware was not available.

- Align dispatch across distance estimation, rotation, and matrix kernels
- Isolate Eigen implementations for each ISA backend
- Preserve portable wheel performance and search accuracy
- Clean up includes and improve static-analysis coverage
- Add regression tests and document dispatch conventions
@gouyt13
gouyt13 merged commit b1e4d5b into main Sep 16, 2026
20 checks passed
@gouyt13
gouyt13 deleted the refactor/unified-kernel-dispatch branch September 16, 2026 04:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant