Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3c7b38f
Port native SIMD to Google Highway: C++ infrastructure
r-devulap May 15, 2026
f108a4c
Port native SIMD to Google Highway: FP32 kernels
r-devulap May 15, 2026
6238715
Port native SIMD to Google Highway: PQ kernels
r-devulap May 15, 2026
d4ed0f9
Port native SIMD to Google Highway: NVQ kernels
r-devulap May 15, 2026
397f712
Always prefer native dp, l2 and cosine
r-devulap May 18, 2026
ced7519
Add new calculatePartialSelfSum
r-devulap May 18, 2026
6510a11
Add Meson and Ninja installation to GitHub Actions workflows
r-devulap May 26, 2026
4190499
Add git submodule initialization to GitHub Actions workflows
r-devulap May 26, 2026
e381500
Exclude .gitmodules from RAT checks
r-devulap May 26, 2026
18a90ce
Remove recursive submodule init from unit tests workflow
r-devulap May 27, 2026
887c4b3
Use DataStax license headers and exclude native build artifacts from RAT
r-devulap May 27, 2026
82d9d17
Remove unnecessary file and duplicate gcc version check
r-devulap May 27, 2026
e0106f5
Fix Shuffle2301/Shuffle1032 comments: diagrams and semantics were swa…
r-devulap May 27, 2026
c2e5068
Fix RAT excludes: add module-relative paths for build/ and third_party/
r-devulap May 27, 2026
00b5eef
Use snake case consistently
r-devulap Jun 8, 2026
18b1b5d
Use --auto-install-deps instead of --auto-install-gcc
r-devulap Jun 8, 2026
1edff51
Configure maven-clean-plugin to remove meson build artifacts
r-devulap Jun 25, 2026
80f3229
Mode meson build directory to jvector-native/target
r-devulap Jun 25, 2026
32a0565
Revert "Configure maven-clean-plugin to remove meson build artifacts"
r-devulap Jun 25, 2026
d940c55
Rename jvector-native/src/main/c -> jvector-native/src/main/native
r-devulap Jun 25, 2026
398caeb
ci: add meson/ninja install and submodule init to run-compaction work…
r-devulap Jun 25, 2026
2053dcb
Adding release notes for PR #668
r-devulap Jun 26, 2026
b0136cb
Add Maven profiles for native builds: debug, debugoptimized, and rele…
r-devulap Jun 24, 2026
2075f2f
refactor(native): introduce X-Macro kernel list to eliminate SIMD boi…
r-devulap Jun 30, 2026
e22aedd
Update release notes and native module README to reflect new changes
r-devulap Jun 30, 2026
92fb22d
native: add AVX3_DL (ICX) and AVX3_SPR (SPR) ISA tiers
r-devulap Jun 30, 2026
63eecd9
add jvector_simd_get_active_isa() API and dispatcher tests
r-devulap Jul 3, 2026
242280a
ci: add avx2 and sse42 ISA cap scenarios to build-avx512 job (jdk24 o…
r-devulap Jul 3, 2026
f1277d3
Run only jvector-tests on jdk 20
r-devulap Jul 3, 2026
690ec67
Update README
r-devulap Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/run-bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ jobs:
- name: Set up GCC
run: |
sudo apt install -y gcc
- name: Install Meson and Ninja
run: |
sudo apt update && sudo apt install -y meson ninja-build
Comment thread
tlwillke marked this conversation as resolved.
- uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v3
Expand Down Expand Up @@ -126,6 +129,9 @@ jobs:
ref: ${{ matrix.branch }}
fetch-depth: 0

- name: Initialize Git Submodules
run: git submodule update --init
Comment thread
r-devulap marked this conversation as resolved.

# ==========================================
# Decode and write the protected dataset catalog
#
Expand Down Expand Up @@ -241,9 +247,15 @@ jobs:
needs: test-avx512
runs-on: ubuntu-latest
steps:
- name: Install Meson and Ninja
Comment thread
r-devulap marked this conversation as resolved.
run: |
sudo apt update && sudo apt install -y meson ninja-build
- name: Checkout repository
uses: actions/checkout@v4

- name: Initialize Git Submodules
run: git submodule update --init

- name: Download all benchmark results
uses: actions/download-artifact@v4
with:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/run-compaction.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ jobs:
steps:
- name: Set up GCC
run: sudo apt install -y gcc
- name: Install Meson and Ninja
run: |
sudo apt update && sudo apt install -y meson ninja-build
- uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v3
Expand All @@ -70,6 +73,9 @@ jobs:
ref: ${{ matrix.branch }}
fetch-depth: 0

- name: Initialize Git Submodules
run: git submodule update --init

- name: Build branch
run: mvn -B -Punix-amd64-profile package --file pom.xml

Expand Down
38 changes: 29 additions & 9 deletions .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@ on:
jobs:
build-avx512:
concurrency:
group: ${{ matrix.isa }}-${{ matrix.jdk }}
group: isa-avx512f-${{ matrix.max_isa }}-${{ matrix.jdk }}
cancel-in-progress: false
strategy:
matrix:
jdk: [ 20, 24 ]
# jdk: [ 24 ]
isa: [ isa-avx512f ]
runs-on: ${{ matrix.isa }}
max_isa: [ avx512f, avx2, sse42 ]
exclude:
- jdk: 20
max_isa: avx2
- jdk: 20
max_isa: sse42
runs-on: isa-avx512f
steps:
- name: verify-avx512
run: |
Expand All @@ -40,7 +45,12 @@ jobs:
- name: Set up GCC
run: |
sudo apt install -y gcc
- name: Install Meson and Ninja
run: |
sudo apt update && sudo apt install -y meson ninja-build
- uses: actions/checkout@v4
- name: Initialize Git Submodules
run: git submodule update --init
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v3
with:
Expand All @@ -56,27 +66,31 @@ jobs:
-Dtest=TestVectorizationProvider
-DTest_RequireSpecificVectorizationProvider=PanamaVectorizationProvider

- name: Test Panama Support (JDK ${{ matrix.jdk }})
- name: Test full suite with Panama (JDK ${{ matrix.jdk }})
if: matrix.jdk == '20'
run: >-
mvn -B -Pjdk20 -pl jvector-tests test -am test
mvn -B -Pjdk20 -pl jvector-tests -am test
-DTest_RequireSpecificVectorizationProvider=PanamaVectorizationProvider

- name: Verify native-access vector support (JDK ${{ matrix.jdk }})
if: matrix.jdk == '24'
env:
JVECTOR_MAX_ISA: ${{ matrix.max_isa }}
run: >-
mvn -B -Punix-amd64-profile -pl jvector-tests -am test
-DTest_RequireSpecificVectorizationProvider=NativeVectorizationProvider
-Dsurefire.failIfNoSpecifiedTests=false
-Dtest=TestVectorizationProvider

- name: Compile, run tests and package (JDK ${{ matrix.jdk }})
- name: Test full suite with native vectorization (JDK ${{ matrix.jdk }})
if: matrix.jdk == '24'
env:
JVECTOR_MAX_ISA: ${{ matrix.max_isa }}
run: >-
mvn -B -Punix-amd64-profile -pl jvector-tests -am test
mvn -B -Punix-amd64-profile test
-DTest_RequireSpecificVectorizationProvider=NativeVectorizationProvider

- name: Test Summary for (ISA:${{ matrix.isa}},JDK${{ matrix.jdk }})
- name: Test Summary for (ISA:isa-avx512f/max:${{ matrix.max_isa }},JDK${{ matrix.jdk }})
if: always()
uses: test-summary/action@v2
with:
Expand All @@ -88,7 +102,7 @@ jobs:
uses: actions/upload-artifact@v4
if: always() # ensures upload happens even if tests fail
with:
name: surefire-results--${{ matrix.isa }}-${{ matrix.jdk }}
name: surefire-results--isa-avx512f-${{ matrix.max_isa }}-${{ matrix.jdk }}
path: "**/target/surefire-reports/**"

build:
Expand All @@ -102,6 +116,8 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Initialize Git Submodules
run: git submodule update --init
- name: Set up JDK
uses: actions/setup-java@v3
with:
Expand All @@ -112,6 +128,10 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt install -y gcc
- name: Install Meson and Ninja
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt update && sudo apt install -y meson ninja-build
- name: Compile, run tests, and package (JDK 22)
run: mvn -B verify
if: matrix.jdk == '22'
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "jvector-native/src/main/native/third_party/highway"]
path = jvector-native/src/main/native/third_party/highway
url = https://github.com/google/highway.git
Comment thread
r-devulap marked this conversation as resolved.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,43 @@ a dependency from any Java 11 code. When run on a Java 20+ JVM with the Vector m
providers will be used. In general, the project is structured to be built with JDK 20+, but when `JAVA_HOME` is set to
Java 11 -> Java 19, certain build features will still be available.

### Cloning

This repository uses a Git submodule for [Google Highway](https://github.com/google/highway), located at
`jvector-native/src/main/native/third_party/highway`. After cloning, initialise it with:

```bash
git submodule update --init
```

Or clone with submodules in one step:

```bash
git clone --recurse-submodules <repo-url>
```

### Building native libraries

The native SIMD library (`libjvector.so`) is built with [Meson](https://mesonbuild.com/) + [Ninja](https://ninja-build.org/)
and requires **g++ 11+**. The entry-point script is
`jvector-native/src/main/native/jextract_vector_simd.sh`. Run it from that directory:

```bash
cd jvector-native/src/main/native
bash jextract_vector_simd.sh
```

On a fresh Ubuntu machine you can install all required dependencies (g++, meson, ninja) and build in one step:

```bash
cd jvector-native/src/main/native
bash jextract_vector_simd.sh --auto-install-deps
```

For other distributions the script will print the install commands needed. See
[`jvector-native/src/main/native/README.md`](./jvector-native/src/main/native/README.md) for full build options,
Maven integration, ISA dispatch details, and how to add new kernels.

Base code is in [jvector-base](./jvector-base) and will be built for Java 11 releases, restricting language features and APIs
appropriately. Code in [jvector-twenty](./jvector-twenty) will be compiled for Java 20 language features/APIs and included in the final
multirelease jar targeting supported JVMs. [jvector-multirelease](./jvector-multirelease) packages [jvector-base](./jvector-base) and [jvector-twenty](./jvector-twenty) as a
Expand Down
82 changes: 82 additions & 0 deletions docs/release notes/4.1.0/668.performance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
### Native SIMD Acceleration via Google Highway

**Description**

This PR introduces a comprehensive native SIMD backend to JVector, porting
**all** Panama Vector API kernels to native code backed by [Google
Highway](https://github.com/google/highway), a portable, header-only C++
library for SIMD operations. Previously, only two kernels in `jvector-native`
were implemented natively; all other SIMD operations relied on the Panama
Vector API as a fallback. Google Highway is currently enabled on **Linux
x86-64** only; Windows and macOS are not yet supported. The native shared
library (`libjvector.so`) is built as a single
binary containing three ISA-specific variants: AVX-512 (Skylake), AVX2
(Haswell), and SSE4.2. At JVM startup, the most suitable ISA is automatically
selected via CPUID detection. SSE4.2 serves as the minimum supported baseline;
CPUs lacking this capability will trigger a `SIGILL` at runtime.

The ported kernels cover all hot paths:

- **FP32 similarity** — `dot_product`, `cosine`, `euclidean`
- **Product Quantization (PQ)** — `assemble_and_sum`, `calculate_partial_sums`,
`pq_decoded_cosine_similarity`
- **Non-uniform Vector Quantization (NVQ)** — `nvq_quantize_8bit`, `nvq_loss`,
`nvq_square_l2_distance_8bit`, and related helpers
- **Element-wise arithmetic** — `add_in_place`, `sub_in_place`, `max`,
`min_in_place`, and others

A new `calculatePartialSelfSum` kernel was also added. On Linux x86-64, when
native vectorization is enabled, the Google Highway backend is used
**exclusively** for all similarity and quantization operations — the Panama
Vector API path is bypassed almost entirely. On other platforms (Windows,
macOS) or architectures (ARM, RISC-V, etc.), the Panama Vector API continues
to provide SIMD acceleration as before; native Highway support for these
platforms is planned for a future release if required.

**Purpose / Impact**

- Consistent, portable SIMD coverage across AVX-512, AVX2, and SSE4.2
without per-target intrinsic duplication
- Automatic ISA selection at startup based on CPUID — no manual configuration
needed
- Eliminates reliance on Panama Vector API for similarity kernels, removing
JDK-version sensitivity for the fast path
- Runtime ISA can be capped for testing via the `JVECTOR_MAX_ISA` environment
variable (`avx3`, `avx2`, or `sse42`)
- **Minimum requirement:** SSE4.2-capable x86-64 CPU; older CPUs will crash
with `SIGILL`

**How to Enable**

Native vectorization is opt-in. Add the following JVM flags to enable it:

```bash
java --enable-native-access=ALL-UNNAMED \
-Djvector.experimental.enable_native_vectorization=true \
-jar your-app.jar
```

Once enabled, `NativeVectorizationProvider` loads `libjvector.so` and the
Highway-backed kernels are used automatically for all similarity and
quantization operations.

To additionally cap the ISA for debugging or benchmarking:

```bash
JVECTOR_MAX_ISA=avx2 java --enable-native-access=ALL-UNNAMED \
-Djvector.experimental.enable_native_vectorization=true \
-jar your-app.jar
```

**Notes**

- The build now uses [Meson](https://mesonbuild.com/) + Ninja to compile the
three ISA variants; see
`jvector-native/src/main/native/jextract_vector_simd.sh` for build and
`jextract` binding-regeneration instructions.
- Google Highway is vendored as a Git submodule under
`jvector-native/src/main/native/third_party/highway` at commit
[`16f5f5c7`](https://github.com/google/highway/commit/16f5f5c768064d0bb52952888a14236bec75c104)
(v1.4.0+15).
- CI workflows (unit tests and compaction) were updated to install Meson/Ninja
and initialise the Highway submodule before building.
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ public CosineDecoder(PQVectors cv, VectorFloat<?> query) {
for (int m = 0; m < pq.getSubspaceCount(); ++m) {
int size = pq.subvectorSizesAndOffsets[m][0];
var codebook = pq.codebooks[m];
for (int j = 0; j < pq.getClusterCount(); ++j) {
partialMagnitudes.set((m * pq.getClusterCount()) + j, VectorUtil.dotProduct(codebook, j * size, codebook, j * size, size));
}
VectorUtil.calculatePartialSelfMagnitudes(codebook, m, size, pq.getClusterCount(), partialMagnitudes);
}
return partialMagnitudes;
});
Expand All @@ -117,9 +115,7 @@ public CosineDecoder(PQVectors cv, VectorFloat<?> query) {
int offset = pq.subvectorSizesAndOffsets[m][1];
int size = pq.subvectorSizesAndOffsets[m][0];
var codebook = pq.codebooks[m];
for (int j = 0; j < pq.getClusterCount(); ++j) {
partialSums.set((m * pq.getClusterCount()) + j, VectorUtil.dotProduct(codebook, j * size, centeredQuery, offset, size));
}
VectorUtil.calculatePartialSums(codebook, m, size, pq.getClusterCount(), centeredQuery, offset, VectorSimilarityFunction.DOT_PRODUCT, partialSums);
}

this.bMagnitude = VectorUtil.dotProduct(centeredQuery, centeredQuery);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ public static void calculatePartialSums(VectorFloat<?> codebook, int codebookInd
impl.calculatePartialSums(codebook, codebookIndex, size, clusterCount, query, offset, vsf, partialSums);
}

public static void calculatePartialSelfMagnitudes(VectorFloat<?> codebook, int codebookIndex, int size, int clusterCount, VectorFloat<?> partialMagnitudes) {
impl.calculatePartialSelfMagnitudes(codebook, codebookIndex, size, clusterCount, partialMagnitudes);
}

/**
* Calculates the maximum value in the vector.
* @param v vector
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ public interface VectorUtilSupport {

void calculatePartialSums(VectorFloat<?> codebook, int codebookIndex, int size, int clusterCount, VectorFloat<?> query, int offset, VectorSimilarityFunction vsf, VectorFloat<?> partialSums);

default void calculatePartialSelfMagnitudes(VectorFloat<?> codebook, int codebookIndex, int size, int clusterCount, VectorFloat<?> partialMagnitudes) {
int codebookBase = codebookIndex * clusterCount;
for (int i = 0; i < clusterCount; i++) {
partialMagnitudes.set(codebookBase + i, dotProduct(codebook, i * size, codebook, i * size, size));
}
}

float max(VectorFloat<?> v);
float min(VectorFloat<?> v);

Expand Down
Loading
Loading