Why the Java Vector API Is on Its 12th Incubation (JEP 537) and What It Is Really Waiting For
Twelve rounds of incubation is a strange record for an API that, by all accounts, works. So what is the delay? The usual answer is “Valhalla”, but that raises a sharper question: if auto-vectorization already handles many loops, who needs this API at all? To find out, I measured both on JDK 27, and the results are more nuanced than either camp expects.
Twelve Rounds, Almost No Change
JEP 537 proposes to re-incubate the Vector API in JDK 27 without substantial change. The history it lists runs from JEP 338 in JDK 16 through JEP 529 in JDK 26. In other words, this is the twelfth consecutive release carrying the same incubating API. According to InfoWorld’s JDK 27 review, the repeat is not because the API has significant issues.

What It Is Waiting For
The JEP states the condition plainly. The API will keep incubating until the necessary Project Valhalla features are available as preview features. Then the team will adapt the API and promote it from incubation to preview. Meanwhile, JEP 529 explains the main change ahead: turning the API’s current value-based classes into real value classes.
Why does that matter? Today a FloatVector is an ordinary heap object with an identity. Therefore, the compiler has to prove that these temporary objects never need to exist, so it can keep the data in a CPU register. That is my summary, not the JEP’s wording, but it explains why the API wants identity-free objects.
| Piece | Status | Source |
|---|---|---|
| JEP 537, Vector API | Targeted to JDK 27, no substantial changes | InfoQ |
| JEP 401, value objects | Preview in JDK 28 (planned March 2027) | The Register |
| JEP 539, strict field initialization | Companion JEP, also in JDK 28 | InfoWorld |
| JEP 401 leaving preview | Brian Goetz called hoping for JDK 29 “optimistic” | The Register |
A Real SIMD Benchmark
Now for the measurement. I tested three kernels on 16,384-element arrays: an elementwise float map, an integer sum, and a float dot product. For each, I compared a plain Java loop (which C2 may auto-vectorize) with a Vector API version. The machine was a shared single-core VM with an Intel Xeon at 2.1 GHz and AVX-512, running Temurin 27.0.1. Each number is the median of five fresh JVMs, each with a 3-second warm-up and 15 timed samples.
You can try the dot-product case yourself. It needs JDK 21 or newer, and the compiler prints an incubator warning, which is normal. I ran this exact block on JDK 27 and JDK 21.
cat > Dot.java <<'EOF'
import jdk.incubator.vector.*;
import java.util.Arrays;
public class Dot {
static final VectorSpecies<Float> S = FloatVector.SPECIES_PREFERRED;
static final int N = 1 << 14;
static float scalar(float[] a, float[] b) {
float s = 0;
for (int i = 0; i < N; i++) s += a[i] * b[i];
return s;
}
static float vector(float[] a, float[] b) {
var acc = FloatVector.zero(S);
for (int i = 0; i < N; i += S.length())
acc = FloatVector.fromArray(S, a, i)
.fma(FloatVector.fromArray(S, b, i), acc);
return acc.reduceLanes(VectorOperators.ADD);
}
static double micros(boolean vec, float[] a, float[] b) {
float sink = 0;
long end = System.nanoTime() + 2_000_000_000L; // 2 s warm-up
while (System.nanoTime() < end) sink += vec ? vector(a, b) : scalar(a, b);
double[] t = new double[11];
for (int j = 0; j < t.length; j++) {
long t0 = System.nanoTime();
for (int n = 0; n < 1000; n++) sink += vec ? vector(a, b) : scalar(a, b);
t[j] = (System.nanoTime() - t0) / 1e6; // microseconds per call
}
Arrays.sort(t);
if (sink == 42) System.out.println();
return t[t.length / 2]; // median
}
public static void main(String[] args) {
float[] a = new float[N], b = new float[N];
for (int i = 0; i < N; i++) { a[i] = i % 7; b[i] = i % 5; }
System.out.println("lanes: " + S.length());
System.out.printf("scalar: %.1f us%n", micros(false, a, b));
System.out.printf("vector: %.1f us%n", micros(true, a, b));
}
}
EOF
java --add-modules jdk.incubator.vector Dot.java
On my machine, repeated runs on JDK 27 printed 13.6 to 18.8 microseconds for scalar and about 1.5 for vector. Your numbers will differ with your CPU, and a shared machine adds noise. Note that the vector version sums in a different order, so its result is not bit-identical to the scalar one.
| Kernel | Variant | Median per call (ns) | Per element (ns) | Range, 5 runs (ns) |
|---|---|---|---|---|
| Float map | Plain loop | 1,796 | 0.110 | 1,670 to 2,388 |
| Float map | Vector API | 2,362 | 0.144 | 2,003 to 2,487 |
| Int sum | Plain loop | 415 | 0.025 | 343 to 443 |
| Int sum | Vector API | 688 | 0.042 | 624 to 705 |
| Float dot | Plain loop | 18,552 | 1.132 | 14,038 to 18,791 |
| Float dot | Hand-unrolled, 8 accumulators | 4,297 | 0.262 | 3,743 to 4,375 |
| Float dot | Vector API | 1,501 | 0.092 | 1,497 to 1,579 |
Own measurement, hand-written harness (not JMH), shared VM, so treat ratios as indicative. Assembly was not inspected

What the Numbers Say
First, the compiler held its own. On the elementwise map and the integer sum, the plain loop beat my Vector API code. I cannot confirm the loops were vectorized without reading the assembly, but they ran far faster than a scalar loop would. Second, the same integer-sum loop took roughly 4,000 to 5,200 ns on JDK 21 in my three trial runs, so auto-vectorization has clearly improved between releases.
However, the float dot product tells a different story. Java float addition is not associative, so the compiler is not free to reorder a running sum. As a result, the plain loop was about 12 times slower than the Vector API, and even eight hand-written accumulators were nearly 3 times slower. The Vector API lets you state explicitly that you accept a different summation order.
The Provocation
Here is the uncomfortable part. If auto-vectorization keeps improving, the Vector API’s real audience shrinks to loops where the language forbids the compiler from helping, such as float reductions, plus code needing masks, shuffles or explicit lane control. That niche is real, but it is narrow, and it makes the wait tolerable. Still, there is a cost: an incubator module needs --add-modules on every run, which keeps it away from many libraries.
Checklist: should you reach for the Vector API?
- You measured a plain loop first and it is the bottleneck.
- The loop involves a float or double reduction, or lane-level control.
- You can accept a changed summation order and a different last digit.
- You can add the incubator module flag to your build and launch scripts.
What We Learned
JEP 537 is the twelfth incubation of the Vector API, and it comes with no substantial changes because the team is waiting for Project Valhalla’s value classes, which arrive as a preview in JDK 28. Our benchmark on JDK 27 showed that the compiler already matched or beat the Vector API on an elementwise map and an integer sum. By contrast, the API was about 12 times faster than a plain float dot-product loop, because strict floating-point ordering blocks auto-vectorization. So the API is not obsolete, but its value sits in a narrow set of cases. Measure your loop first, and reach for the Vector API only where the compiler cannot follow.

