This is an automated email from the ASF dual-hosted git repository.
clintropolis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new e61fddc3dbe feat: add simd add/sub/mul expressions (#19512)
e61fddc3dbe is described below
commit e61fddc3dbe1af432b60f6bf30afd4187cb4e539
Author: Clint Wylie <[email protected]>
AuthorDate: Mon Jun 1 14:06:15 2026 -0700
feat: add simd add/sub/mul expressions (#19512)
---
benchmarks/pom.xml | 20 +++++
.../benchmark/query/SqlExpressionBenchmark.java | 16 ++++
docs/operations/java.md | 6 +-
examples/bin/run-java | 1 +
.../druid/indexing/overlord/ForkingTaskRunner.java | 3 +-
pom.xml | 15 ++++
.../druid/math/expr/ExpressionProcessing.java | 22 ++++-
.../math/expr/ExpressionProcessingConfig.java | 13 ++-
.../SimpleVectorMathBivariateProcessorFactory.java | 55 ++++++++++++
.../math/expr/vector/VectorMathProcessors.java | 14 +++-
.../vector/simd/SimdDoubleDoubleAddProcessor.java | 93 ++++++++++++++++++++
.../vector/simd/SimdDoubleDoubleMulProcessor.java | 93 ++++++++++++++++++++
.../vector/simd/SimdDoubleDoubleProcessor.java | 95 +++++++++++++++++++++
.../vector/simd/SimdDoubleDoubleSubProcessor.java | 93 ++++++++++++++++++++
.../vector/simd/SimdDoubleLongAddProcessor.java | 96 +++++++++++++++++++++
.../vector/simd/SimdDoubleLongMulProcessor.java | 96 +++++++++++++++++++++
.../expr/vector/simd/SimdDoubleLongProcessor.java | 98 ++++++++++++++++++++++
.../vector/simd/SimdDoubleLongSubProcessor.java | 96 +++++++++++++++++++++
.../vector/simd/SimdLongDoubleAddProcessor.java | 96 +++++++++++++++++++++
.../vector/simd/SimdLongDoubleMulProcessor.java | 96 +++++++++++++++++++++
.../expr/vector/simd/SimdLongDoubleProcessor.java | 98 ++++++++++++++++++++++
.../vector/simd/SimdLongDoubleSubProcessor.java | 96 +++++++++++++++++++++
.../expr/vector/simd/SimdLongLongAddProcessor.java | 93 ++++++++++++++++++++
.../expr/vector/simd/SimdLongLongMulProcessor.java | 93 ++++++++++++++++++++
.../expr/vector/simd/SimdLongLongProcessor.java | 96 +++++++++++++++++++++
.../expr/vector/simd/SimdLongLongSubProcessor.java | 93 ++++++++++++++++++++
.../math/expr/vector/simd/SimdProcessors.java | 98 ++++++++++++++++++++++
.../expr/vector/simd/SimdSupportedBinaryOp.java | 36 ++++++++
.../math/expr/VectorExprResultConsistencyTest.java | 37 ++++----
.../VectorExprResultConsistencyVectorApiTest.java | 42 ++++++++++
30 files changed, 1875 insertions(+), 24 deletions(-)
diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml
index 429b9323777..c02c2166f11 100644
--- a/benchmarks/pom.xml
+++ b/benchmarks/pom.xml
@@ -244,6 +244,26 @@
<build>
<plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.14.1</version>
+ <inherited>true</inherited>
+ <configuration>
+ <release>${maven.compiler.release}</release>
+ <annotationProcessors>
+
<annotationProcessor>org.openjdk.jmh.generators.BenchmarkProcessor</annotationProcessor>
+ </annotationProcessors>
+ <annotationProcessorPaths combine.children="append">
+ <path>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-generator-annprocess</artifactId>
+ <version>${jmh.version}</version>
+ </path>
+ </annotationProcessorPaths>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
diff --git
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
index 8988973f982..0ef6395a1fc 100644
---
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
+++
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
@@ -21,12 +21,15 @@ package org.apache.druid.benchmark.query;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import org.apache.druid.math.expr.ExpressionProcessing;
import org.apache.druid.query.QueryContexts;
import org.apache.druid.query.groupby.GroupByQueryConfig;
import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
@@ -171,6 +174,9 @@ public class SqlExpressionBenchmark extends
SqlBaseQueryBenchmark
})
private String deferExpressionDimensions;
+ @Param({"false", "true"})
+ private boolean useVectorApi;
+
@Param({
// non-expression reference
"0",
@@ -238,6 +244,16 @@ public class SqlExpressionBenchmark extends
SqlBaseQueryBenchmark
})
private String query;
+ @Setup(Level.Trial)
+ public void setupExpressionProcessing()
+ {
+ if (useVectorApi) {
+ ExpressionProcessing.initializeForVectorApiTests();
+ } else {
+ ExpressionProcessing.initializeForTests();
+ }
+ }
+
@Override
public String getQuery()
{
diff --git a/docs/operations/java.md b/docs/operations/java.md
index f4a8c029db2..c6117e1f426 100644
--- a/docs/operations/java.md
+++ b/docs/operations/java.md
@@ -85,5 +85,9 @@ added. There are many ways of doing this. Choose the one that
works best for you
--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED \
--add-opens=java.base/java.io=ALL-UNNAMED \
--add-opens=java.base/java.lang=ALL-UNNAMED \
---add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
+--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED \
+--add-modules=jdk.incubator.vector
```
+
+The `--add-modules=jdk.incubator.vector` flag is optional, but adding it makes
the JDK's incubator Vector API available
+to Druid to support `druid.expressions.useVectorApi=true`.
diff --git a/examples/bin/run-java b/examples/bin/run-java
index 80190d0a793..5a30cd54fbd 100755
--- a/examples/bin/run-java
+++ b/examples/bin/run-java
@@ -43,6 +43,7 @@ then
--add-opens=java.base/java.io=ALL-UNNAMED \
--add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED \
+ --add-modules=jdk.incubator.vector \
"$@"
else
exec "$JAVA_BIN" "$@"
diff --git
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
index fb09cb5f154..52c2dcc7ba3 100644
---
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
+++
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
@@ -115,7 +115,8 @@ public class ForkingTaskRunner
"--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED",
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.base/java.lang=ALL-UNNAMED",
- "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED"
+ "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED",
+ "--add-modules=jdk.incubator.vector"
);
private final ForkingTaskRunnerConfig config;
diff --git a/pom.xml b/pom.xml
index d871b7cdb28..dfd23d67061 100644
--- a/pom.xml
+++ b/pom.xml
@@ -164,6 +164,9 @@
<!-- required for certain EqualsVerifier tests (not required in
production) -->
--add-opens=java.base/java.util=ALL-UNNAMED
+
+ <!-- required for SIMD for druid.expressions.useVectorApi -->
+ --add-modules=jdk.incubator.vector
</jdk.strong.encapsulation.argLine>
<jdk.security.manager.allow.argLine><!-- empty placeholder
--></jdk.security.manager.allow.argLine>
<repoOrgId>maven.org</repoOrgId>
@@ -1794,6 +1797,8 @@
<exclude>**/*_jmhType_*.class</exclude>
<exclude>**/*_jmhTest_*.class</exclude>
<exclude>**/*_generated*.class</exclude>
+ <!-- forbidden-apis can't resolve jdk.incubator.vector
classes from its own classpath -->
+ <exclude>**/math/expr/vector/simd/Simd*.class</exclude>
</excludes>
<suppressAnnotations>
<annotation>**.SuppressForbidden</annotation>
@@ -2143,6 +2148,11 @@
<!-- HadoopFsWrapper javadocs cannot be generated due
to missing annotations -->
<excludePackageNames>org.apache.hadoop.fs</excludePackageNames>
+
+ <!-- required for SIMD expression vector processors
that import jdk.incubator.vector -->
+ <additionalOptions>
+
<additionalOption>--add-modules=jdk.incubator.vector</additionalOption>
+ </additionalOptions>
</configuration>
</plugin>
<plugin>
@@ -2152,6 +2162,9 @@
<inherited>true</inherited>
<configuration>
<release>${maven.compiler.release}</release>
+ <compilerArgs>
+ <arg>--add-modules=jdk.incubator.vector</arg>
+ </compilerArgs>
</configuration>
</plugin>
<plugin>
@@ -2212,6 +2225,8 @@
<arg>-J--add-exports=java.base/sun.nio.ch=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
+ <arg>-J--add-modules=jdk.incubator.vector</arg>
+ <arg>--add-modules=jdk.incubator.vector</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessing.java
b/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessing.java
index 7b2e1d26ae4..68b5cef8cc1 100644
---
a/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessing.java
+++
b/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessing.java
@@ -45,13 +45,19 @@ public class ExpressionProcessing
@VisibleForTesting
public static void initializeForTests()
{
- INSTANCE = new ExpressionProcessingConfig(null, null, null);
+ INSTANCE = new ExpressionProcessingConfig(null, null, null, null);
}
@VisibleForTesting
public static void initializeForHomogenizeNullMultiValueStrings()
{
- INSTANCE = new ExpressionProcessingConfig(null, true, null);
+ INSTANCE = new ExpressionProcessingConfig(null, true, null, null);
+ }
+
+ @VisibleForTesting
+ public static void initializeForVectorApiTests()
+ {
+ INSTANCE = new ExpressionProcessingConfig(null, null, null, true);
}
/**
@@ -81,6 +87,18 @@ public class ExpressionProcessing
return INSTANCE.allowVectorizeFallback();
}
+ /**
+ * Whether {@link org.apache.druid.math.expr.vector.ExprVectorProcessor}
implementations may dispatch to specialized
+ * {@code jdk.incubator.vector} (SIMD) variants for supported math
operations. Off by default; opt-in via
+ * {@link ExpressionProcessingConfig#USE_VECTOR_API}. Requires the JVM to be
started with
+ * {@code --add-modules=jdk.incubator.vector}, which Druid already adds to
its standard launch arguments.
+ */
+ public static boolean useVectorApi()
+ {
+ checkInitialized();
+ return INSTANCE.useVectorApi();
+ }
+
private static void checkInitialized()
{
// this should only be null in a unit test context, in production this
will be injected by the null handling module
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessingConfig.java
b/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessingConfig.java
index 78c2ecfcbbc..d24600a302a 100644
---
a/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessingConfig.java
+++
b/processing/src/main/java/org/apache/druid/math/expr/ExpressionProcessingConfig.java
@@ -34,6 +34,7 @@ public class ExpressionProcessingConfig
public static final String HOMOGENIZE_NULL_MULTIVALUE_STRING_ARRAYS =
"druid.expressions.homogenizeNullMultiValueStringArrays";
public static final String ALLOW_VECTORIZE_FALLBACK =
"druid.expressions.allowVectorizeFallback";
+ public static final String USE_VECTOR_API = "druid.expressions.useVectorApi";
@JsonProperty("processArraysAsMultiValueStrings")
private final boolean processArraysAsMultiValueStrings;
@@ -44,11 +45,15 @@ public class ExpressionProcessingConfig
@JsonProperty("allowVectorizeFallback")
private final boolean allowVectorizeFallback;
+ @JsonProperty("useVectorApi")
+ private final boolean useVectorApi;
+
@JsonCreator
public ExpressionProcessingConfig(
@JsonProperty("processArraysAsMultiValueStrings") @Nullable Boolean
processArraysAsMultiValueStrings,
@JsonProperty("homogenizeNullMultiValueStringArrays") @Nullable Boolean
homogenizeNullMultiValueStringArrays,
- @JsonProperty("allowVectorizeFallback") @Nullable Boolean
allowVectorizeFallback
+ @JsonProperty("allowVectorizeFallback") @Nullable Boolean
allowVectorizeFallback,
+ @JsonProperty("useVectorApi") @Nullable Boolean useVectorApi
)
{
this.processArraysAsMultiValueStrings = getWithPropertyFallbackFalse(
@@ -64,6 +69,7 @@ public class ExpressionProcessingConfig
ALLOW_VECTORIZE_FALLBACK,
"true"
);
+ this.useVectorApi = getWithPropertyFallbackFalse(useVectorApi,
USE_VECTOR_API);
}
public boolean processArraysAsMultiValueStrings()
@@ -81,6 +87,11 @@ public class ExpressionProcessingConfig
return allowVectorizeFallback;
}
+ public boolean useVectorApi()
+ {
+ return useVectorApi;
+ }
+
private static boolean getWithPropertyFallbackFalse(@Nullable Boolean value,
String property)
{
return getWithPropertyFallback(value, property, "false");
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathBivariateProcessorFactory.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathBivariateProcessorFactory.java
index 230b2e44f35..deebbeb565f 100644
---
a/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathBivariateProcessorFactory.java
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathBivariateProcessorFactory.java
@@ -20,10 +20,15 @@
package org.apache.druid.math.expr.vector;
import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionProcessing;
import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoubleLongFunction;
import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoublesFunction;
import
org.apache.druid.math.expr.vector.functional.DoubleBivariateLongDoubleFunction;
import org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
+import org.apache.druid.math.expr.vector.simd.SimdProcessors;
+import org.apache.druid.math.expr.vector.simd.SimdSupportedBinaryOp;
+
+import javax.annotation.Nullable;
/**
* Make a 2 argument, math processor with the following type rules
@@ -31,6 +36,10 @@ import
org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
* long, double -> double
* double, long -> double
* double, double -> double
+ *
+ * If a non-null {@link SimdSupportedBinaryOp} is supplied to the constructor
and
+ * {@link ExpressionProcessing#useVectorApi()} is true, this factory will
return SIMD-specialized processors backed
+ * by the JDK incubator {@code jdk.incubator.vector} API instead of the
standard scalar implementations.
*/
public class SimpleVectorMathBivariateProcessorFactory extends
VectorMathBivariateProcessorFactory
{
@@ -38,6 +47,8 @@ public class SimpleVectorMathBivariateProcessorFactory
extends VectorMathBivaria
private final DoubleBivariateLongDoubleFunction longDoubleFunction;
private final DoubleBivariateDoubleLongFunction doubleLongFunction;
private final DoubleBivariateDoublesFunction doublesFunction;
+ @Nullable
+ private final SimdSupportedBinaryOp simdOp;
protected SimpleVectorMathBivariateProcessorFactory(
LongBivariateLongsFunction longsFunction,
@@ -45,16 +56,36 @@ public class SimpleVectorMathBivariateProcessorFactory
extends VectorMathBivaria
DoubleBivariateDoubleLongFunction doubleLongFunction,
DoubleBivariateDoublesFunction doublesFunction
)
+ {
+ this(longsFunction, longDoubleFunction, doubleLongFunction,
doublesFunction, null);
+ }
+
+ protected SimpleVectorMathBivariateProcessorFactory(
+ LongBivariateLongsFunction longsFunction,
+ DoubleBivariateLongDoubleFunction longDoubleFunction,
+ DoubleBivariateDoubleLongFunction doubleLongFunction,
+ DoubleBivariateDoublesFunction doublesFunction,
+ @Nullable SimdSupportedBinaryOp simdOp
+ )
{
this.longsFunction = longsFunction;
this.longDoubleFunction = longDoubleFunction;
this.doubleLongFunction = doubleLongFunction;
this.doublesFunction = doublesFunction;
+ this.simdOp = simdOp;
}
@Override
public final ExprVectorProcessor<long[]>
longsProcessor(Expr.VectorInputBindingInspector inspector, Expr left, Expr
right)
{
+ if (simdOp != null && ExpressionProcessing.useVectorApi()) {
+ return SimdProcessors.makeLongLong(
+ left.asVectorProcessor(inspector),
+ right.asVectorProcessor(inspector),
+ simdOp,
+ longsFunction
+ );
+ }
return new LongBivariateLongsFunctionVectorProcessor(
left.asVectorProcessor(inspector),
right.asVectorProcessor(inspector),
@@ -69,6 +100,14 @@ public class SimpleVectorMathBivariateProcessorFactory
extends VectorMathBivaria
Expr right
)
{
+ if (simdOp != null && ExpressionProcessing.useVectorApi()) {
+ return SimdProcessors.makeLongDouble(
+ left.asVectorProcessor(inspector),
+ right.asVectorProcessor(inspector),
+ simdOp,
+ longDoubleFunction
+ );
+ }
return new DoubleBivariateLongDoubleFunctionVectorProcessor(
left.asVectorProcessor(inspector),
right.asVectorProcessor(inspector),
@@ -83,6 +122,14 @@ public class SimpleVectorMathBivariateProcessorFactory
extends VectorMathBivaria
Expr right
)
{
+ if (simdOp != null && ExpressionProcessing.useVectorApi()) {
+ return SimdProcessors.makeDoubleLong(
+ left.asVectorProcessor(inspector),
+ right.asVectorProcessor(inspector),
+ simdOp,
+ doubleLongFunction
+ );
+ }
return new DoubleBivariateDoubleLongFunctionVectorProcessor(
left.asVectorProcessor(inspector),
right.asVectorProcessor(inspector),
@@ -97,6 +144,14 @@ public class SimpleVectorMathBivariateProcessorFactory
extends VectorMathBivaria
Expr right
)
{
+ if (simdOp != null && ExpressionProcessing.useVectorApi()) {
+ return SimdProcessors.makeDoubleDouble(
+ left.asVectorProcessor(inspector),
+ right.asVectorProcessor(inspector),
+ simdOp,
+ doublesFunction
+ );
+ }
return new DoubleBivariateDoublesFunctionVectorProcessor(
left.asVectorProcessor(inspector),
right.asVectorProcessor(inspector),
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/VectorMathProcessors.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/VectorMathProcessors.java
index c12ebc55eaa..4a26f814153 100644
---
a/processing/src/main/java/org/apache/druid/math/expr/vector/VectorMathProcessors.java
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/VectorMathProcessors.java
@@ -23,6 +23,7 @@ import com.google.common.math.LongMath;
import com.google.common.primitives.Ints;
import org.apache.druid.math.expr.ExpressionValidationException;
import org.apache.druid.math.expr.Function;
+import org.apache.druid.math.expr.vector.simd.SimdSupportedBinaryOp;
public class VectorMathProcessors
{
@@ -300,7 +301,7 @@ public class VectorMathProcessors
public Add()
{
- super(Long::sum, Double::sum, Double::sum, Double::sum);
+ super(Long::sum, Double::sum, Double::sum, Double::sum,
SimdSupportedBinaryOp.ADD);
}
}
@@ -314,7 +315,8 @@ public class VectorMathProcessors
(left, right) -> left - right,
(left, right) -> (double) left - right,
(left, right) -> left - (double) right,
- (left, right) -> left - right
+ (left, right) -> left - right,
+ SimdSupportedBinaryOp.SUB
);
}
}
@@ -325,7 +327,13 @@ public class VectorMathProcessors
public Multiply()
{
- super(Multiply::multiply, Multiply::multiply, Multiply::multiply,
Multiply::multiply);
+ super(
+ Multiply::multiply,
+ Multiply::multiply,
+ Multiply::multiply,
+ Multiply::multiply,
+ SimdSupportedBinaryOp.MUL
+ );
}
private static long multiply(long x, long y)
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleAddProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleAddProcessor.java
new file mode 100644
index 00000000000..d476468cb07
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleAddProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoublesFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[], double[]) -> double[]} addition.
The op is hardcoded to
+ * {@link DoubleVector#add} so the JIT statically resolves it to the
platform's double-add intrinsic.
+ */
+public final class SimdDoubleDoubleAddProcessor extends
SimdDoubleDoubleProcessor
+{
+ public SimdDoubleDoubleAddProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoublesFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ double[] leftInput,
+ double[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va = DoubleVector.fromArray(SPECIES, leftInput, i);
+ final DoubleVector vb = DoubleVector.fromArray(SPECIES, rightInput, i);
+ va.add(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(SPECIES, rightNulls, i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(SPECIES, rightNulls, i);
+ }
+ final DoubleVector va = DoubleVector.fromArray(SPECIES, leftInput, i);
+ final DoubleVector vb = DoubleVector.fromArray(SPECIES, rightInput, i);
+ va.add(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleMulProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleMulProcessor.java
new file mode 100644
index 00000000000..56cf53e3309
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleMulProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoublesFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[], double[]) -> double[]}
multiplication. The op is hardcoded to
+ * {@link DoubleVector#mul} so the JIT statically resolves it to the
platform's double-multiply intrinsic.
+ */
+public final class SimdDoubleDoubleMulProcessor extends
SimdDoubleDoubleProcessor
+{
+ public SimdDoubleDoubleMulProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoublesFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ double[] leftInput,
+ double[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va = DoubleVector.fromArray(SPECIES, leftInput, i);
+ final DoubleVector vb = DoubleVector.fromArray(SPECIES, rightInput, i);
+ va.mul(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(SPECIES, rightNulls, i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(SPECIES, rightNulls, i);
+ }
+ final DoubleVector va = DoubleVector.fromArray(SPECIES, leftInput, i);
+ final DoubleVector vb = DoubleVector.fromArray(SPECIES, rightInput, i);
+ va.mul(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleProcessor.java
new file mode 100644
index 00000000000..8f3eeebac2c
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleProcessor.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.VectorSpecies;
+import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionType;
+import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
+import org.apache.druid.math.expr.vector.ExprEvalDoubleVector;
+import org.apache.druid.math.expr.vector.ExprEvalVector;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoublesFunction;
+
+import javax.annotation.Nullable;
+
+/**
+ * Abstract base for SIMD processors that compute {@code (double[], double[])
-> double[]} ops. See
+ * {@link SimdLongLongProcessor} for the design rationale.
+ */
+abstract class SimdDoubleDoubleProcessor implements
ExprVectorProcessor<double[]>
+{
+ static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
+
+ private final ExprVectorProcessor<double[]> left;
+ private final ExprVectorProcessor<double[]> right;
+ final DoubleBivariateDoublesFunction scalarFallback;
+ final double[] outValues;
+ final boolean[] outNulls;
+
+ protected SimdDoubleDoubleProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoublesFunction scalarFallback
+ )
+ {
+ this.left = CastToTypeVectorProcessor.cast(left, ExpressionType.DOUBLE);
+ this.right = CastToTypeVectorProcessor.cast(right, ExpressionType.DOUBLE);
+ this.scalarFallback = scalarFallback;
+ this.outValues = new double[this.left.maxVectorSize()];
+ this.outNulls = new boolean[this.left.maxVectorSize()];
+ }
+
+ @Override
+ public final ExprEvalVector<double[]> evalVector(Expr.VectorInputBinding
bindings)
+ {
+ final ExprEvalVector<double[]> lhs = left.evalVector(bindings);
+ final ExprEvalVector<double[]> rhs = right.evalVector(bindings);
+ processVector(
+ lhs.values(),
+ rhs.values(),
+ lhs.getNullVector(),
+ rhs.getNullVector(),
+ bindings.getCurrentVectorSize()
+ );
+ return new ExprEvalDoubleVector(outValues, outNulls);
+ }
+
+ protected abstract void processVector(
+ double[] leftInput,
+ double[] rightInput,
+ @Nullable boolean[] leftNulls,
+ @Nullable boolean[] rightNulls,
+ int currentSize
+ );
+
+ @Override
+ public final ExpressionType getOutputType()
+ {
+ return ExpressionType.DOUBLE;
+ }
+
+ @Override
+ public final int maxVectorSize()
+ {
+ return outValues.length;
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleSubProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleSubProcessor.java
new file mode 100644
index 00000000000..9f290240bce
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleSubProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoublesFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[], double[]) -> double[]}
subtraction. The op is hardcoded to
+ * {@link DoubleVector#sub} so the JIT statically resolves it to the
platform's double-subtract intrinsic.
+ */
+public final class SimdDoubleDoubleSubProcessor extends
SimdDoubleDoubleProcessor
+{
+ public SimdDoubleDoubleSubProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoublesFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ double[] leftInput,
+ double[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va = DoubleVector.fromArray(SPECIES, leftInput, i);
+ final DoubleVector vb = DoubleVector.fromArray(SPECIES, rightInput, i);
+ va.sub(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(SPECIES, rightNulls, i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(SPECIES, rightNulls, i);
+ }
+ final DoubleVector va = DoubleVector.fromArray(SPECIES, leftInput, i);
+ final DoubleVector vb = DoubleVector.fromArray(SPECIES, rightInput, i);
+ va.sub(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongAddProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongAddProcessor.java
new file mode 100644
index 00000000000..5d1eb74f0d9
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongAddProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoubleLongFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[], long[]) -> double[]} addition. The
op is hardcoded to
+ * {@link DoubleVector#add} so the JIT statically resolves it to the
platform's double-add intrinsic.
+ */
+public final class SimdDoubleLongAddProcessor extends SimdDoubleLongProcessor
+{
+ public SimdDoubleLongAddProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoubleLongFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ double[] leftInput,
+ long[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = DOUBLE_SPECIES.length();
+ final int upperBound = DOUBLE_SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va = DoubleVector.fromArray(DOUBLE_SPECIES,
leftInput, i);
+ final DoubleVector vb =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, rightInput,
i).castShape(DOUBLE_SPECIES, 0);
+ va.add(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(DOUBLE_SPECIES, rightNulls,
i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, rightNulls, i);
+ }
+ final DoubleVector va = DoubleVector.fromArray(DOUBLE_SPECIES,
leftInput, i);
+ final DoubleVector vb =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, rightInput,
i).castShape(DOUBLE_SPECIES, 0);
+ va.add(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongMulProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongMulProcessor.java
new file mode 100644
index 00000000000..b593799ee26
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongMulProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoubleLongFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[], long[]) -> double[]}
multiplication. The op is hardcoded to
+ * {@link DoubleVector#mul} so the JIT statically resolves it to the
platform's double-multiply intrinsic.
+ */
+public final class SimdDoubleLongMulProcessor extends SimdDoubleLongProcessor
+{
+ public SimdDoubleLongMulProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoubleLongFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ double[] leftInput,
+ long[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = DOUBLE_SPECIES.length();
+ final int upperBound = DOUBLE_SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va = DoubleVector.fromArray(DOUBLE_SPECIES,
leftInput, i);
+ final DoubleVector vb =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, rightInput,
i).castShape(DOUBLE_SPECIES, 0);
+ va.mul(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(DOUBLE_SPECIES, rightNulls,
i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, rightNulls, i);
+ }
+ final DoubleVector va = DoubleVector.fromArray(DOUBLE_SPECIES,
leftInput, i);
+ final DoubleVector vb =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, rightInput,
i).castShape(DOUBLE_SPECIES, 0);
+ va.mul(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongProcessor.java
new file mode 100644
index 00000000000..e3e705d656a
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongProcessor.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorSpecies;
+import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionType;
+import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
+import org.apache.druid.math.expr.vector.ExprEvalDoubleVector;
+import org.apache.druid.math.expr.vector.ExprEvalVector;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoubleLongFunction;
+
+import javax.annotation.Nullable;
+
+/**
+ * Abstract base for SIMD processors that compute {@code (double[], long[]) ->
double[]} ops. The long lane is
+ * widened to {@link DoubleVector} via {@code
castShape(DoubleVector.SPECIES_PREFERRED, 0)} in each subclass's hot
+ * loop. See {@link SimdLongLongProcessor} for the design rationale.
+ */
+abstract class SimdDoubleLongProcessor implements ExprVectorProcessor<double[]>
+{
+ static final VectorSpecies<Long> LONG_SPECIES = LongVector.SPECIES_PREFERRED;
+ static final VectorSpecies<Double> DOUBLE_SPECIES =
DoubleVector.SPECIES_PREFERRED;
+
+ private final ExprVectorProcessor<double[]> left;
+ private final ExprVectorProcessor<long[]> right;
+ final DoubleBivariateDoubleLongFunction scalarFallback;
+ final double[] outValues;
+ final boolean[] outNulls;
+
+ protected SimdDoubleLongProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoubleLongFunction scalarFallback
+ )
+ {
+ this.left = CastToTypeVectorProcessor.cast(left, ExpressionType.DOUBLE);
+ this.right = CastToTypeVectorProcessor.cast(right, ExpressionType.LONG);
+ this.scalarFallback = scalarFallback;
+ this.outValues = new double[this.left.maxVectorSize()];
+ this.outNulls = new boolean[this.left.maxVectorSize()];
+ }
+
+ @Override
+ public final ExprEvalVector<double[]> evalVector(Expr.VectorInputBinding
bindings)
+ {
+ final ExprEvalVector<double[]> lhs = left.evalVector(bindings);
+ final ExprEvalVector<long[]> rhs = right.evalVector(bindings);
+ processVector(
+ lhs.values(),
+ rhs.values(),
+ lhs.getNullVector(),
+ rhs.getNullVector(),
+ bindings.getCurrentVectorSize()
+ );
+ return new ExprEvalDoubleVector(outValues, outNulls);
+ }
+
+ protected abstract void processVector(
+ double[] leftInput,
+ long[] rightInput,
+ @Nullable boolean[] leftNulls,
+ @Nullable boolean[] rightNulls,
+ int currentSize
+ );
+
+ @Override
+ public final ExpressionType getOutputType()
+ {
+ return ExpressionType.DOUBLE;
+ }
+
+ @Override
+ public final int maxVectorSize()
+ {
+ return outValues.length;
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongSubProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongSubProcessor.java
new file mode 100644
index 00000000000..97da5a718f6
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongSubProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoubleLongFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[], long[]) -> double[]} subtraction.
The op is hardcoded to
+ * {@link DoubleVector#sub} so the JIT statically resolves it to the
platform's double-subtract intrinsic.
+ */
+public final class SimdDoubleLongSubProcessor extends SimdDoubleLongProcessor
+{
+ public SimdDoubleLongSubProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateDoubleLongFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ double[] leftInput,
+ long[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = DOUBLE_SPECIES.length();
+ final int upperBound = DOUBLE_SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va = DoubleVector.fromArray(DOUBLE_SPECIES,
leftInput, i);
+ final DoubleVector vb =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, rightInput,
i).castShape(DOUBLE_SPECIES, 0);
+ va.sub(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(DOUBLE_SPECIES, rightNulls,
i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, rightNulls, i);
+ }
+ final DoubleVector va = DoubleVector.fromArray(DOUBLE_SPECIES,
leftInput, i);
+ final DoubleVector vb =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, rightInput,
i).castShape(DOUBLE_SPECIES, 0);
+ va.sub(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleAddProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleAddProcessor.java
new file mode 100644
index 00000000000..bd077a05a02
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleAddProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateLongDoubleFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[], double[]) -> double[]} addition. The
op is hardcoded to
+ * {@link DoubleVector#add} so the JIT statically resolves it to the
platform's double-add intrinsic.
+ */
+public final class SimdLongDoubleAddProcessor extends SimdLongDoubleProcessor
+{
+ public SimdLongDoubleAddProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateLongDoubleFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ long[] leftInput,
+ double[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = DOUBLE_SPECIES.length();
+ final int upperBound = DOUBLE_SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, leftInput,
i).castShape(DOUBLE_SPECIES, 0);
+ final DoubleVector vb = DoubleVector.fromArray(DOUBLE_SPECIES,
rightInput, i);
+ va.add(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(DOUBLE_SPECIES, rightNulls,
i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, rightNulls, i);
+ }
+ final DoubleVector va =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, leftInput,
i).castShape(DOUBLE_SPECIES, 0);
+ final DoubleVector vb = DoubleVector.fromArray(DOUBLE_SPECIES,
rightInput, i);
+ va.add(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleMulProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleMulProcessor.java
new file mode 100644
index 00000000000..2d211e26b7e
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleMulProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateLongDoubleFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[], double[]) -> double[]}
multiplication. The op is hardcoded to
+ * {@link DoubleVector#mul} so the JIT statically resolves it to the
platform's double-multiply intrinsic.
+ */
+public final class SimdLongDoubleMulProcessor extends SimdLongDoubleProcessor
+{
+ public SimdLongDoubleMulProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateLongDoubleFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ long[] leftInput,
+ double[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = DOUBLE_SPECIES.length();
+ final int upperBound = DOUBLE_SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, leftInput,
i).castShape(DOUBLE_SPECIES, 0);
+ final DoubleVector vb = DoubleVector.fromArray(DOUBLE_SPECIES,
rightInput, i);
+ va.mul(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(DOUBLE_SPECIES, rightNulls,
i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, rightNulls, i);
+ }
+ final DoubleVector va =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, leftInput,
i).castShape(DOUBLE_SPECIES, 0);
+ final DoubleVector vb = DoubleVector.fromArray(DOUBLE_SPECIES,
rightInput, i);
+ va.mul(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleProcessor.java
new file mode 100644
index 00000000000..366354f82b2
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleProcessor.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorSpecies;
+import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionType;
+import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
+import org.apache.druid.math.expr.vector.ExprEvalDoubleVector;
+import org.apache.druid.math.expr.vector.ExprEvalVector;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateLongDoubleFunction;
+
+import javax.annotation.Nullable;
+
+/**
+ * Abstract base for SIMD processors that compute {@code (long[], double[]) ->
double[]} ops. The long lane is
+ * widened to {@link DoubleVector} via {@code
castShape(DoubleVector.SPECIES_PREFERRED, 0)} in each subclass's hot
+ * loop. See {@link SimdLongLongProcessor} for the design rationale.
+ */
+abstract class SimdLongDoubleProcessor implements ExprVectorProcessor<double[]>
+{
+ static final VectorSpecies<Long> LONG_SPECIES = LongVector.SPECIES_PREFERRED;
+ static final VectorSpecies<Double> DOUBLE_SPECIES =
DoubleVector.SPECIES_PREFERRED;
+
+ private final ExprVectorProcessor<long[]> left;
+ private final ExprVectorProcessor<double[]> right;
+ final DoubleBivariateLongDoubleFunction scalarFallback;
+ final double[] outValues;
+ final boolean[] outNulls;
+
+ protected SimdLongDoubleProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateLongDoubleFunction scalarFallback
+ )
+ {
+ this.left = CastToTypeVectorProcessor.cast(left, ExpressionType.LONG);
+ this.right = CastToTypeVectorProcessor.cast(right, ExpressionType.DOUBLE);
+ this.scalarFallback = scalarFallback;
+ this.outValues = new double[this.left.maxVectorSize()];
+ this.outNulls = new boolean[this.left.maxVectorSize()];
+ }
+
+ @Override
+ public final ExprEvalVector<double[]> evalVector(Expr.VectorInputBinding
bindings)
+ {
+ final ExprEvalVector<long[]> lhs = left.evalVector(bindings);
+ final ExprEvalVector<double[]> rhs = right.evalVector(bindings);
+ processVector(
+ lhs.values(),
+ rhs.values(),
+ lhs.getNullVector(),
+ rhs.getNullVector(),
+ bindings.getCurrentVectorSize()
+ );
+ return new ExprEvalDoubleVector(outValues, outNulls);
+ }
+
+ protected abstract void processVector(
+ long[] leftInput,
+ double[] rightInput,
+ @Nullable boolean[] leftNulls,
+ @Nullable boolean[] rightNulls,
+ int currentSize
+ );
+
+ @Override
+ public final ExpressionType getOutputType()
+ {
+ return ExpressionType.DOUBLE;
+ }
+
+ @Override
+ public final int maxVectorSize()
+ {
+ return outValues.length;
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleSubProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleSubProcessor.java
new file mode 100644
index 00000000000..33c8602cf88
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleSubProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateLongDoubleFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[], double[]) -> double[]} subtraction.
The op is hardcoded to
+ * {@link DoubleVector#sub} so the JIT statically resolves it to the
platform's double-subtract intrinsic.
+ */
+public final class SimdLongDoubleSubProcessor extends SimdLongDoubleProcessor
+{
+ public SimdLongDoubleSubProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ DoubleBivariateLongDoubleFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ long[] leftInput,
+ double[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = DOUBLE_SPECIES.length();
+ final int upperBound = DOUBLE_SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final DoubleVector va =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, leftInput,
i).castShape(DOUBLE_SPECIES, 0);
+ final DoubleVector vb = DoubleVector.fromArray(DOUBLE_SPECIES,
rightInput, i);
+ va.sub(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(DOUBLE_SPECIES, rightNulls,
i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(DOUBLE_SPECIES, rightNulls, i);
+ }
+ final DoubleVector va =
+ (DoubleVector) LongVector.fromArray(LONG_SPECIES, leftInput,
i).castShape(DOUBLE_SPECIES, 0);
+ final DoubleVector vb = DoubleVector.fromArray(DOUBLE_SPECIES,
rightInput, i);
+ va.sub(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongAddProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongAddProcessor.java
new file mode 100644
index 00000000000..f5e0298af09
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongAddProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[], long[]) -> long[]} addition. The op
is hardcoded to {@link LongVector#add}
+ * so the JIT statically resolves it to the platform's long-add intrinsic.
+ */
+public final class SimdLongLongAddProcessor extends SimdLongLongProcessor
+{
+ public SimdLongLongAddProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ LongBivariateLongsFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ long[] leftInput,
+ long[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final LongVector va = LongVector.fromArray(SPECIES, leftInput, i);
+ final LongVector vb = LongVector.fromArray(SPECIES, rightInput, i);
+ va.add(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Long> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(SPECIES, rightNulls, i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(SPECIES, rightNulls, i);
+ }
+ final LongVector va = LongVector.fromArray(SPECIES, leftInput, i);
+ final LongVector vb = LongVector.fromArray(SPECIES, rightInput, i);
+ va.add(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongMulProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongMulProcessor.java
new file mode 100644
index 00000000000..32e8e8aa751
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongMulProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[], long[]) -> long[]} multiplication.
The op is hardcoded to
+ * {@link LongVector#mul} so the JIT statically resolves it to the platform's
long-multiply intrinsic.
+ */
+public final class SimdLongLongMulProcessor extends SimdLongLongProcessor
+{
+ public SimdLongLongMulProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ LongBivariateLongsFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ long[] leftInput,
+ long[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final LongVector va = LongVector.fromArray(SPECIES, leftInput, i);
+ final LongVector vb = LongVector.fromArray(SPECIES, rightInput, i);
+ va.mul(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Long> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(SPECIES, rightNulls, i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(SPECIES, rightNulls, i);
+ }
+ final LongVector va = LongVector.fromArray(SPECIES, leftInput, i);
+ final LongVector vb = LongVector.fromArray(SPECIES, rightInput, i);
+ va.mul(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongProcessor.java
new file mode 100644
index 00000000000..999f4149fac
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorSpecies;
+import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionType;
+import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
+import org.apache.druid.math.expr.vector.ExprEvalLongVector;
+import org.apache.druid.math.expr.vector.ExprEvalVector;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
+
+import javax.annotation.Nullable;
+
+/**
+ * Abstract base for SIMD processors that compute {@code (long[], long[]) ->
long[]} ops. Each concrete subclass
+ * (one per op) overrides {@link #processVector} with a hot loop that calls a
statically-resolved {@link LongVector}
+ * method (e.g. {@code va.add(vb)}) so the JIT emits the corresponding SIMD
intrinsic.
+ */
+abstract class SimdLongLongProcessor implements ExprVectorProcessor<long[]>
+{
+ static final VectorSpecies<Long> SPECIES = LongVector.SPECIES_PREFERRED;
+
+ private final ExprVectorProcessor<long[]> left;
+ private final ExprVectorProcessor<long[]> right;
+ final LongBivariateLongsFunction scalarFallback;
+ final long[] outValues;
+ final boolean[] outNulls;
+
+ protected SimdLongLongProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ LongBivariateLongsFunction scalarFallback
+ )
+ {
+ this.left = CastToTypeVectorProcessor.cast(left, ExpressionType.LONG);
+ this.right = CastToTypeVectorProcessor.cast(right, ExpressionType.LONG);
+ this.scalarFallback = scalarFallback;
+ this.outValues = new long[this.left.maxVectorSize()];
+ this.outNulls = new boolean[this.left.maxVectorSize()];
+ }
+
+ @Override
+ public final ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding
bindings)
+ {
+ final ExprEvalVector<long[]> lhs = left.evalVector(bindings);
+ final ExprEvalVector<long[]> rhs = right.evalVector(bindings);
+ processVector(
+ lhs.values(),
+ rhs.values(),
+ lhs.getNullVector(),
+ rhs.getNullVector(),
+ bindings.getCurrentVectorSize()
+ );
+ return new ExprEvalLongVector(outValues, outNulls);
+ }
+
+ protected abstract void processVector(
+ long[] leftInput,
+ long[] rightInput,
+ @Nullable boolean[] leftNulls,
+ @Nullable boolean[] rightNulls,
+ int currentSize
+ );
+
+ @Override
+ public final ExpressionType getOutputType()
+ {
+ return ExpressionType.LONG;
+ }
+
+ @Override
+ public final int maxVectorSize()
+ {
+ return outValues.length;
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongSubProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongSubProcessor.java
new file mode 100644
index 00000000000..ab85396463d
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongLongSubProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[], long[]) -> long[]} subtraction. The
op is hardcoded to {@link LongVector#sub}
+ * so the JIT statically resolves it to the platform's long-subtract intrinsic.
+ */
+public final class SimdLongLongSubProcessor extends SimdLongLongProcessor
+{
+ public SimdLongLongSubProcessor(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ LongBivariateLongsFunction scalarFallback
+ )
+ {
+ super(left, right, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(
+ long[] leftInput,
+ long[] rightInput,
+ boolean[] leftNulls,
+ boolean[] rightNulls,
+ int currentSize
+ )
+ {
+ final boolean hasLeftNulls = leftNulls != null;
+ final boolean hasRightNulls = rightNulls != null;
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ if (!hasLeftNulls && !hasRightNulls) {
+ for (; i < upperBound; i += laneCount) {
+ final LongVector va = LongVector.fromArray(SPECIES, leftInput, i);
+ final LongVector vb = LongVector.fromArray(SPECIES, rightInput, i);
+ va.sub(vb).intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Long> nm;
+ if (hasLeftNulls && hasRightNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i)
+ .or(VectorMask.fromArray(SPECIES, rightNulls, i));
+ } else if (hasLeftNulls) {
+ nm = VectorMask.fromArray(SPECIES, leftNulls, i);
+ } else {
+ nm = VectorMask.fromArray(SPECIES, rightNulls, i);
+ }
+ final LongVector va = LongVector.fromArray(SPECIES, leftInput, i);
+ final LongVector vb = LongVector.fromArray(SPECIES, rightInput, i);
+ va.sub(vb).intoArray(outValues, i);
+ nm.intoArray(outNulls, i);
+ }
+ for (; i < currentSize; i++) {
+ final boolean isNull = (hasLeftNulls && leftNulls[i]) ||
(hasRightNulls && rightNulls[i]);
+ outNulls[i] = isNull;
+ if (!isNull) {
+ outValues[i] = scalarFallback.process(leftInput[i], rightInput[i]);
+ }
+ }
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdProcessors.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdProcessors.java
new file mode 100644
index 00000000000..d8d74021c7a
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdProcessors.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+import org.apache.druid.error.DruidException;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoubleLongFunction;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateDoublesFunction;
+import
org.apache.druid.math.expr.vector.functional.DoubleBivariateLongDoubleFunction;
+import org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
+
+/**
+ * Dispatch table from a {@link SimdSupportedBinaryOp} identifier to a
concrete, op-specialized SIMD processor.
+ * One class per op and type-combo so the JIT sees a monomorphic call site for
the SIMD operation in each hot loop.
+ */
+public final class SimdProcessors
+{
+ private SimdProcessors()
+ {
+ }
+
+ public static ExprVectorProcessor<long[]> makeLongLong(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ SimdSupportedBinaryOp op,
+ LongBivariateLongsFunction scalarFallback
+ )
+ {
+ return switch (op) {
+ case ADD -> new SimdLongLongAddProcessor(left, right, scalarFallback);
+ case SUB -> new SimdLongLongSubProcessor(left, right, scalarFallback);
+ case MUL -> new SimdLongLongMulProcessor(left, right, scalarFallback);
+ default -> throw DruidException.defensive("Unsupported SIMD binary
op[%s]", op);
+ };
+ }
+
+ public static ExprVectorProcessor<double[]> makeDoubleDouble(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ SimdSupportedBinaryOp op,
+ DoubleBivariateDoublesFunction scalarFallback
+ )
+ {
+ return switch (op) {
+ case ADD -> new SimdDoubleDoubleAddProcessor(left, right,
scalarFallback);
+ case SUB -> new SimdDoubleDoubleSubProcessor(left, right,
scalarFallback);
+ case MUL -> new SimdDoubleDoubleMulProcessor(left, right,
scalarFallback);
+ default -> throw DruidException.defensive("Unsupported SIMD binary
op[%s]", op);
+ };
+ }
+
+ public static ExprVectorProcessor<double[]> makeLongDouble(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ SimdSupportedBinaryOp op,
+ DoubleBivariateLongDoubleFunction scalarFallback
+ )
+ {
+ return switch (op) {
+ case ADD -> new SimdLongDoubleAddProcessor(left, right, scalarFallback);
+ case SUB -> new SimdLongDoubleSubProcessor(left, right, scalarFallback);
+ case MUL -> new SimdLongDoubleMulProcessor(left, right, scalarFallback);
+ default -> throw DruidException.defensive("Unsupported SIMD binary
op[%s]", op);
+ };
+ }
+
+ public static ExprVectorProcessor<double[]> makeDoubleLong(
+ ExprVectorProcessor<?> left,
+ ExprVectorProcessor<?> right,
+ SimdSupportedBinaryOp op,
+ DoubleBivariateDoubleLongFunction scalarFallback
+ )
+ {
+ return switch (op) {
+ case ADD -> new SimdDoubleLongAddProcessor(left, right, scalarFallback);
+ case SUB -> new SimdDoubleLongSubProcessor(left, right, scalarFallback);
+ case MUL -> new SimdDoubleLongMulProcessor(left, right, scalarFallback);
+ default -> throw DruidException.defensive("Unsupported SIMD binary
op[%s]", op);
+ };
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdSupportedBinaryOp.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdSupportedBinaryOp.java
new file mode 100644
index 00000000000..953571ba4d3
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdSupportedBinaryOp.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr.vector.simd;
+
+/**
+ * Identifies which binary math operations have a {@code jdk.incubator.vector}
(SIMD) specialization. Used by
+ * {@link
org.apache.druid.math.expr.vector.SimpleVectorMathBivariateProcessorFactory}
subclasses to declare that
+ * their operation can be dispatched to a SIMD variant when the user enables
+ * {@link
org.apache.druid.math.expr.ExpressionProcessingConfig#USE_VECTOR_API}.
+ *
+ * Deliberately does not reference any {@code jdk.incubator.vector} types so
that callers wiring the enum into
+ * factories do not need the incubator module visible.
+ */
+public enum SimdSupportedBinaryOp
+{
+ ADD,
+ SUB,
+ MUL
+}
diff --git
a/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
index 7f21e489f3d..9ffe5da3ace 100644
---
a/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
+++
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
@@ -67,7 +67,7 @@ public class VectorExprResultConsistencyTest extends
InitializedNullHandlingTest
{
private static final Logger log = new
Logger(VectorExprResultConsistencyTest.class);
private static final int NUM_ITERATIONS = 10;
- private static final int VECTOR_SIZE = 4;
+ private static final List<Integer> VECTOR_SIZES = List.of(3, 8, 17, 67);
private static final Map<String, String> LOOKUP = Map.of(
@@ -764,16 +764,18 @@ public class VectorExprResultConsistencyTest extends
InitializedNullHandlingTest
final int numIterations
)
{
- for (int iter = 0; iter < numIterations; iter++) {
- assertEvalsMatch(
- expr,
- parsed,
- makeSequentialBinding(
- VECTOR_SIZE,
- types,
- -2 + (iter * VECTOR_SIZE) // include negative numbers and zero
- )
- );
+ for (int vectorSize : VECTOR_SIZES) {
+ for (int iter = 0; iter < numIterations; iter++) {
+ assertEvalsMatch(
+ expr,
+ parsed,
+ makeSequentialBinding(
+ vectorSize,
+ types,
+ -2 + (iter * vectorSize) // include negative numbers and zero
+ )
+ );
+ }
}
}
@@ -784,8 +786,10 @@ public class VectorExprResultConsistencyTest extends
InitializedNullHandlingTest
final int numIterations
)
{
- for (int iterations = 0; iterations < numIterations; iterations++) {
- assertEvalsMatch(expr, parsed, makeRandomizedBindings(VECTOR_SIZE,
types));
+ for (int vectorSize : VECTOR_SIZES) {
+ for (int iterations = 0; iterations < numIterations; iterations++) {
+ assertEvalsMatch(expr, parsed, makeRandomizedBindings(vectorSize,
types));
+ }
}
}
@@ -808,7 +812,8 @@ public class VectorExprResultConsistencyTest extends
InitializedNullHandlingTest
);
if (vectorEval.isValue() && nonVectorEval.isValue()) {
- for (int i = 0; i < VECTOR_SIZE; i++) {
+ final int vectorSize = bindings.lhs.length;
+ for (int i = 0; i < vectorSize; i++) {
final String message = StringUtils.format(
"Values do not match for row[%s] for expression[%s], bindings[%s]",
i,
@@ -1000,9 +1005,9 @@ public class VectorExprResultConsistencyTest extends
InitializedNullHandlingTest
@Nullable ExpressionType outputType
)
{
- final Object[] exprValues = new Object[VECTOR_SIZE];
+ final Object[] exprValues = new Object[bindings.length];
- for (int i = 0; i < VECTOR_SIZE; i++) {
+ for (int i = 0; i < bindings.length; i++) {
ExprEval<?> eval;
try {
eval = expr.eval(bindings[i]);
diff --git
a/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyVectorApiTest.java
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyVectorApiTest.java
new file mode 100644
index 00000000000..a14190796a2
--- /dev/null
+++
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyVectorApiTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.math.expr;
+
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * Re-runs every {@link VectorExprResultConsistencyTest} case with the SIMD
({@code jdk.incubator.vector}) expression
+ * vector processors enabled, ensuring the SIMD specializations agree with the
non-vectorized reference.
+ */
+public class VectorExprResultConsistencyVectorApiTest extends
VectorExprResultConsistencyTest
+{
+ @Before
+ public void enableVectorApi()
+ {
+ ExpressionProcessing.initializeForVectorApiTests();
+ }
+
+ @After
+ public void resetExpressionProcessing()
+ {
+ ExpressionProcessing.initializeForTests();
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]