Skip to content

Add AVX-512 f32 dot product and squared L2 kernels - #145674

Merged
ChrisHegarty merged 14 commits into
elastic:mainfrom
ChrisHegarty:f32-avx512-kernels-2
Apr 10, 2026
Merged

ChrisHegarty merged 14 commits into
elastic:mainfrom
ChrisHegarty:f32-avx512-kernels-2

Conversation

@ChrisHegarty

Copy link
Copy Markdown
Contributor

The vec_dotf32 and vec_sqrf32 functions currently use AVX2 (256-bit ymm registers) in vec_1.cpp. This PR adds vec_dotf32_2, vec_sqrf32_2, and their bulk/bulk_offsets variants to vec_2.cpp using __m512 / _mm512_fmadd_ps with 4 accumulators and masked tail handling. The runtime dispatch in JdkVectorLibrary already falls back from _2 to _1 suffix, so these are picked up automatically on AVX-512 capable hardware.

vec_dotf32 and vec_sqrf32 currently use AVX2 (256-bit ymm registers)
in vec_1.cpp.

Add vec_dotf32_2, vec_sqrf32_2, and their bulk/bulk_offsets variants
to vec_2.cpp using __m512 / _mm512_fmadd_ps with 4 accumulators and
masked tail handling. The runtime dispatch in JdkVectorLibrary already
falls back from _2 to _1 suffix, so these are picked up automatically
on AVX-512 capable hardware.
@ChrisHegarty ChrisHegarty added >refactoring :Search Relevance/Vectors Vector search Team:Search Relevance Meta label for the Search Relevance team in Elasticsearch labels Apr 3, 2026
@ldematte

ldematte commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

I have inspected the kernels to see if there is more that can be gained; TL;DR: the advantage over AVX2 is purely due to wider loads -- we are able to load more data per cycle. With 32-bit elements, these simple kernels are load-bound, not compute bound.

@ldematte

ldematte commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

The current implementation is at (or very near) the theoretical performance limit for a single-pair distance computation on all current x86 microarchitectures.

A float32 dot product loads two vectors (a and b) and computes sum(a[i] * b[i]). In AVX-512, the inner loop is:

vmovups       zmm8, [rdi + rax*4]         ; load 16 floats from a
vfmadd231ps   zmm3, zmm8, [rsi + rax*4]  ; load 16 floats from b, acc += a * b

Two 512-bit loads feed one fused multiply-add. That's 2 loads per FMA. Every x86 CPU — AMD and Intel alike — has at most 2 load ports. A dot product needs 2 loads to produce 1 FMA. This means the load ports are always fully occupied, while the FMA units are at most 50% utilized.

No amount of loop unrolling, accumulator count tuning, or instruction scheduling can change this — the ratio is dictated by the algorithm, not the implementation.

Evidence from benchmarks (Zen 5, c8a.xlarge)

Three independent observations confirm the load-bound diagnosis:

  1. Dot product ≈ Euclidean distance. Euclidean requires an extra vsubps per element, yet performs identically. The extra ALU operation is absorbed by the idle FADD ports while the load ports pace execution.

  2. 4 accumulators ≈ 8 accumulators. Doubling the independent FMA chains (which should hide more pipeline latency) produces no improvement — the out-of-order engine already overlaps FMA stalls with load port stalls.

  3. Timings scale linearly with data size, tracking the L1 load bandwidth floor plus a fixed overhead (FFM downcall + horizontal reduce).

This holds across all current x86 CPUs:

Processor Load ports FMA ports (512-bit) Bottleneck
Skylake-X 2 × 512-bit/cycle 2 × 512-bit/cycle Load-bound
Ice Lake (client) 2 × 512-bit/cycle 1 × 512-bit/cycle Compute-bound*
Ice Lake-SP 2 × 512-bit/cycle 2 × 512-bit/cycle Load-bound
Sapphire Rapids 2 × 512-bit/cycle 2 × 512-bit/cycle Load-bound
Zen 4 2 × 256-bit/cycle 2 × 256-bit FMA/cycle Load-bound
Zen 5 2 × 512-bit/cycle 2 × 512-bit/cycle Load-bound

On Zen 5 and other AVX-512 capable processors, load bandwidth doubled alongside register width:

  • AVX-2: 2 × 256-bit = 64 bytes/cycle
  • AVX-512: 2 × 512-bit = 128 bytes/cycle

The wider loads process more data per cycle, so we see a speedup wrt AVX2, but the load ports are still the bottleneck in both cases -- this is the most amount of data that can be moved through these CPUs.

@ldematte ldematte 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.

One minor thing, and requires a version bump, but otherwise this LGTM

Comment thread libs/simdvec/native/src/vec/c/amd64/vec_2.cpp Outdated
@ChrisHegarty
ChrisHegarty marked this pull request as ready for review April 9, 2026 10:28
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-search-relevance (Team:Search Relevance)

@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Hi @ChrisHegarty, I've created a changelog YAML for you.

@ChrisHegarty
ChrisHegarty merged commit 2a1e7b7 into elastic:main Apr 10, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

>enhancement :Search Relevance/Vectors Vector search Team:Search Relevance Meta label for the Search Relevance team in Elasticsearch v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants