This is an automated email from the ASF dual-hosted git repository.
kfaraz 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 1ebdf5ff7f9 feat: simd negate and abs (#19703)
1ebdf5ff7f9 is described below
commit 1ebdf5ff7f98b4257a2027cdc09dd15ac177dce4
Author: Clint Wylie <[email protected]>
AuthorDate: Wed Jul 22 01:22:10 2026 -0700
feat: simd negate and abs (#19703)
opt-in `jdk.incubator.vector` specializations for negate and abs unary
expressions.
---
.../benchmark/query/SqlExpressionBenchmark.java | 17 ++++-
...SimpleVectorMathUnivariateProcessorFactory.java | 29 +++++++-
.../math/expr/vector/VectorMathProcessors.java | 6 +-
.../expr/vector/simd/SimdDoubleAbsProcessor.java | 57 +++++++++++++++
.../expr/vector/simd/SimdDoubleNegProcessor.java | 57 +++++++++++++++
.../expr/vector/simd/SimdDoubleUnaryProcessor.java | 79 +++++++++++++++++++++
.../expr/vector/simd/SimdLongAbsProcessor.java | 57 +++++++++++++++
.../expr/vector/simd/SimdLongNegProcessor.java | 57 +++++++++++++++
.../expr/vector/simd/SimdLongUnaryProcessor.java | 80 ++++++++++++++++++++++
.../math/expr/vector/simd/SimdProcessors.java | 33 ++++++++-
.../expr/vector/simd/SimdSupportedUnaryOp.java | 35 ++++++++++
11 files changed, 500 insertions(+), 7 deletions(-)
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 5bc0019e723..43a1bb3413c 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
@@ -168,7 +168,15 @@ public class SqlExpressionBenchmark extends
SqlBaseQueryBenchmark
// 61-63: cast
"SELECT CAST(string1 as BIGINT) + CAST(string3 as DOUBLE) + long3,
COUNT(*) FROM expressions GROUP BY 1 ORDER BY 2",
"SELECT COUNT(*), SUM(CAST(string1 as BIGINT) + CAST(string3 as BIGINT))
FROM expressions WHERE double3 < 1010.0 AND double3 > 100.0",
- "SELECT COUNT(*) FROM expressions WHERE __time >= TIMESTAMP '2000-01-01
00:00:00' AND __time < TIMESTAMP '2000-01-02 00:00:00' AND
(UPPER(COALESCE(string3,'')) LIKE '1%' OR TRIM(UPPER(COALESCE(string3,'')))
LIKE '1%' OR SUBSTRING(UPPER(COALESCE(string3,'')),1,1) IN
('1','2','3','4','5') OR ('X' || UPPER(COALESCE(string3,''))) LIKE 'X1%') AND
(UPPER(COALESCE(string5,'')) LIKE '2%' OR TRIM(UPPER(COALESCE(string5,'')))
LIKE '2%' OR SUBSTRING(UPPER(COALESCE(string5,'')),1,1) IN ('1','2 [...]
+ "SELECT COUNT(*) FROM expressions WHERE __time >= TIMESTAMP '2000-01-01
00:00:00' AND __time < TIMESTAMP '2000-01-02 00:00:00' AND
(UPPER(COALESCE(string3,'')) LIKE '1%' OR TRIM(UPPER(COALESCE(string3,'')))
LIKE '1%' OR SUBSTRING(UPPER(COALESCE(string3,'')),1,1) IN
('1','2','3','4','5') OR ('X' || UPPER(COALESCE(string3,''))) LIKE 'X1%') AND
(UPPER(COALESCE(string5,'')) LIKE '2%' OR TRIM(UPPER(COALESCE(string5,'')))
LIKE '2%' OR SUBSTRING(UPPER(COALESCE(string5,'')),1,1) IN ('1','2 [...]
+ // 64,65: unary negate on a double column (companion to 15 which does
long negate)
+ "SELECT SUM(-double1) FROM expressions",
+ "SELECT SUM(-double4) FROM expressions",
+ // 66,67: unary abs on long and double columns
+ "SELECT SUM(ABS(long4)) FROM expressions",
+ "SELECT SUM(ABS(double1)) FROM expressions",
+ // 68: unary abs of a binary subtraction (composes SIMD sub with SIMD
abs)
+ "SELECT SUM(ABS(long1 - long4)) FROM expressions"
);
@Param({
@@ -248,7 +256,12 @@ public class SqlExpressionBenchmark extends
SqlBaseQueryBenchmark
"60",
"61",
"62",
- "63"
+ "63",
+ "64",
+ "65",
+ "66",
+ "67",
+ "68"
})
private String query;
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathUnivariateProcessorFactory.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathUnivariateProcessorFactory.java
index 8c1c041b123..581de8a6c63 100644
---
a/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathUnivariateProcessorFactory.java
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/SimpleVectorMathUnivariateProcessorFactory.java
@@ -20,32 +20,56 @@
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.DoubleUnivariateDoubleFunction;
import org.apache.druid.math.expr.vector.functional.LongUnivariateLongFunction;
+import org.apache.druid.math.expr.vector.simd.SimdProcessors;
+import org.apache.druid.math.expr.vector.simd.SimdSupportedUnaryOp;
+
+import javax.annotation.Nullable;
/**
* Make a 1 argument math processor with the following type rules
* long -> long
* double -> double
- * using simple scalar functions {@link LongUnivariateLongFunction} and {@link
DoubleUnivariateDoubleFunction}
+ * using simple scalar functions {@link LongUnivariateLongFunction} and {@link
DoubleUnivariateDoubleFunction}.
+ *
+ * If a non-null {@link SimdSupportedUnaryOp} 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 SimpleVectorMathUnivariateProcessorFactory extends
VectorMathUnivariateProcessorFactory
{
private final LongUnivariateLongFunction longFunction;
private final DoubleUnivariateDoubleFunction doubleFunction;
+ @Nullable
+ private final SimdSupportedUnaryOp simdOp;
public SimpleVectorMathUnivariateProcessorFactory(
LongUnivariateLongFunction longFunction,
DoubleUnivariateDoubleFunction doubleFunction
)
+ {
+ this(longFunction, doubleFunction, null);
+ }
+
+ protected SimpleVectorMathUnivariateProcessorFactory(
+ LongUnivariateLongFunction longFunction,
+ DoubleUnivariateDoubleFunction doubleFunction,
+ @Nullable SimdSupportedUnaryOp simdOp
+ )
{
this.longFunction = longFunction;
this.doubleFunction = doubleFunction;
+ this.simdOp = simdOp;
}
@Override
public final ExprVectorProcessor<long[]>
longProcessor(Expr.VectorInputBindingInspector inspector, Expr arg)
{
+ if (simdOp != null && ExpressionProcessing.useVectorApi()) {
+ return SimdProcessors.makeLongUnary(arg.asVectorProcessor(inspector),
simdOp, longFunction);
+ }
return new LongUnivariateLongFunctionVectorProcessor(
arg.asVectorProcessor(inspector),
longFunction
@@ -55,6 +79,9 @@ public class SimpleVectorMathUnivariateProcessorFactory
extends VectorMathUnivar
@Override
public final ExprVectorProcessor<double[]>
doubleProcessor(Expr.VectorInputBindingInspector inspector, Expr arg)
{
+ if (simdOp != null && ExpressionProcessing.useVectorApi()) {
+ return SimdProcessors.makeDoubleUnary(arg.asVectorProcessor(inspector),
simdOp, doubleFunction);
+ }
return new DoubleUnivariateDoubleFunctionVectorProcessor(
arg.asVectorProcessor(inspector),
doubleFunction
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 3b4f3b8cb30..17b9a629faf 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
@@ -24,6 +24,7 @@ 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;
+import org.apache.druid.math.expr.vector.simd.SimdSupportedUnaryOp;
public class VectorMathProcessors
{
@@ -411,7 +412,8 @@ public class VectorMathProcessors
{
super(
input -> -input,
- input -> -input
+ input -> -input,
+ SimdSupportedUnaryOp.NEG
);
}
}
@@ -542,7 +544,7 @@ public class VectorMathProcessors
public Abs()
{
- super(Math::abs, Math::abs);
+ super(Math::abs, Math::abs, SimdSupportedUnaryOp.ABS);
}
}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleAbsProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleAbsProcessor.java
new file mode 100644
index 00000000000..d11aecf3b71
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleAbsProcessor.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleUnivariateDoubleFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[]) -> double[]} absolute value. The
op is hardcoded to
+ * {@link DoubleVector#abs} so the JIT statically resolves it to the
platform's double-abs intrinsic.
+ */
+public final class SimdDoubleAbsProcessor extends SimdDoubleUnaryProcessor
+{
+ public SimdDoubleAbsProcessor(ExprVectorProcessor<?> input,
DoubleUnivariateDoubleFunction scalarFallback)
+ {
+ super(input, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(double[] input, boolean[] inputNulls, int
currentSize)
+ {
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ for (; i < upperBound; i += laneCount) {
+ DoubleVector.fromArray(SPECIES, input, i).abs().intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(input[i]);
+ }
+ if (inputNulls == null) {
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ System.arraycopy(inputNulls, 0, outNulls, 0, currentSize);
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleNegProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleNegProcessor.java
new file mode 100644
index 00000000000..37b093b849c
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleNegProcessor.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.functional.DoubleUnivariateDoubleFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (double[]) -> double[]} negation. The op is
hardcoded to {@link DoubleVector#neg}
+ * so the JIT statically resolves it to the platform's double-negate intrinsic.
+ */
+public final class SimdDoubleNegProcessor extends SimdDoubleUnaryProcessor
+{
+ public SimdDoubleNegProcessor(ExprVectorProcessor<?> input,
DoubleUnivariateDoubleFunction scalarFallback)
+ {
+ super(input, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(double[] input, boolean[] inputNulls, int
currentSize)
+ {
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ for (; i < upperBound; i += laneCount) {
+ DoubleVector.fromArray(SPECIES, input, i).neg().intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(input[i]);
+ }
+ if (inputNulls == null) {
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ System.arraycopy(inputNulls, 0, outNulls, 0, currentSize);
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleUnaryProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleUnaryProcessor.java
new file mode 100644
index 00000000000..20527f68634
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleUnaryProcessor.java
@@ -0,0 +1,79 @@
+/*
+ * 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.DoubleUnivariateDoubleFunction;
+
+import javax.annotation.Nullable;
+
+/**
+ * Abstract base for SIMD processors that compute {@code (double[]) ->
double[]} unary ops. See
+ * {@link SimdLongUnaryProcessor} for the design rationale.
+ */
+abstract class SimdDoubleUnaryProcessor implements
ExprVectorProcessor<double[]>
+{
+ static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
+
+ private final ExprVectorProcessor<double[]> input;
+ final DoubleUnivariateDoubleFunction scalarFallback;
+ final double[] outValues;
+ final boolean[] outNulls;
+
+ protected SimdDoubleUnaryProcessor(
+ ExprVectorProcessor<?> input,
+ DoubleUnivariateDoubleFunction scalarFallback
+ )
+ {
+ this.input = CastToTypeVectorProcessor.cast(input, ExpressionType.DOUBLE);
+ this.scalarFallback = scalarFallback;
+ this.outValues = new double[this.input.maxVectorSize()];
+ this.outNulls = new boolean[this.input.maxVectorSize()];
+ }
+
+ @Override
+ public final ExprEvalVector<double[]> evalVector(Expr.VectorInputBinding
bindings)
+ {
+ final ExprEvalVector<double[]> lhs = input.evalVector(bindings);
+ processVector(lhs.values(), lhs.getNullVector(),
bindings.getCurrentVectorSize());
+ return new ExprEvalDoubleVector(outValues, outNulls);
+ }
+
+ protected abstract void processVector(double[] input, @Nullable boolean[]
inputNulls, 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/SimdLongAbsProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongAbsProcessor.java
new file mode 100644
index 00000000000..b5489cbdc52
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongAbsProcessor.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import org.apache.druid.math.expr.vector.functional.LongUnivariateLongFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[]) -> long[]} absolute value. The op is
hardcoded to {@link LongVector#abs}
+ * so the JIT statically resolves it to the platform's long-abs intrinsic.
+ */
+public final class SimdLongAbsProcessor extends SimdLongUnaryProcessor
+{
+ public SimdLongAbsProcessor(ExprVectorProcessor<?> input,
LongUnivariateLongFunction scalarFallback)
+ {
+ super(input, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(long[] input, boolean[] inputNulls, int
currentSize)
+ {
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ for (; i < upperBound; i += laneCount) {
+ LongVector.fromArray(SPECIES, input, i).abs().intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(input[i]);
+ }
+ if (inputNulls == null) {
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ System.arraycopy(inputNulls, 0, outNulls, 0, currentSize);
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongNegProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongNegProcessor.java
new file mode 100644
index 00000000000..03ba4a344b8
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongNegProcessor.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import org.apache.druid.math.expr.vector.functional.LongUnivariateLongFunction;
+
+import java.util.Arrays;
+
+/**
+ * SIMD specialization of {@code (long[]) -> long[]} negation. The op is
hardcoded to {@link LongVector#neg}
+ * so the JIT statically resolves it to the platform's long-negate intrinsic.
+ */
+public final class SimdLongNegProcessor extends SimdLongUnaryProcessor
+{
+ public SimdLongNegProcessor(ExprVectorProcessor<?> input,
LongUnivariateLongFunction scalarFallback)
+ {
+ super(input, scalarFallback);
+ }
+
+ @Override
+ protected void processVector(long[] input, boolean[] inputNulls, int
currentSize)
+ {
+ final int laneCount = SPECIES.length();
+ final int upperBound = SPECIES.loopBound(currentSize);
+ int i = 0;
+ for (; i < upperBound; i += laneCount) {
+ LongVector.fromArray(SPECIES, input, i).neg().intoArray(outValues, i);
+ }
+ for (; i < currentSize; i++) {
+ outValues[i] = scalarFallback.process(input[i]);
+ }
+ if (inputNulls == null) {
+ Arrays.fill(outNulls, 0, currentSize, false);
+ } else {
+ System.arraycopy(inputNulls, 0, outNulls, 0, currentSize);
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongUnaryProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongUnaryProcessor.java
new file mode 100644
index 00000000000..e36c6adeb17
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongUnaryProcessor.java
@@ -0,0 +1,80 @@
+/*
+ * 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.LongUnivariateLongFunction;
+
+import javax.annotation.Nullable;
+
+/**
+ * Abstract base for SIMD processors that compute {@code (long[]) -> long[]}
unary 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.neg()} or {@code va.abs()}) so the JIT emits the
corresponding SIMD intrinsic.
+ */
+abstract class SimdLongUnaryProcessor implements ExprVectorProcessor<long[]>
+{
+ static final VectorSpecies<Long> SPECIES = LongVector.SPECIES_PREFERRED;
+
+ private final ExprVectorProcessor<long[]> input;
+ final LongUnivariateLongFunction scalarFallback;
+ final long[] outValues;
+ final boolean[] outNulls;
+
+ protected SimdLongUnaryProcessor(
+ ExprVectorProcessor<?> input,
+ LongUnivariateLongFunction scalarFallback
+ )
+ {
+ this.input = CastToTypeVectorProcessor.cast(input, ExpressionType.LONG);
+ this.scalarFallback = scalarFallback;
+ this.outValues = new long[this.input.maxVectorSize()];
+ this.outNulls = new boolean[this.input.maxVectorSize()];
+ }
+
+ @Override
+ public final ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding
bindings)
+ {
+ final ExprEvalVector<long[]> lhs = input.evalVector(bindings);
+ processVector(lhs.values(), lhs.getNullVector(),
bindings.getCurrentVectorSize());
+ return new ExprEvalLongVector(outValues, outNulls);
+ }
+
+ protected abstract void processVector(long[] input, @Nullable boolean[]
inputNulls, 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/SimdProcessors.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdProcessors.java
index 55ca4c81076..c7ce4878ee8 100644
---
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
@@ -24,11 +24,14 @@ 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.DoubleUnivariateDoubleFunction;
import org.apache.druid.math.expr.vector.functional.LongBivariateLongsFunction;
+import org.apache.druid.math.expr.vector.functional.LongUnivariateLongFunction;
/**
- * 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.
+ * Dispatch table from {@link SimdSupportedBinaryOp} / {@link
SimdSupportedUnaryOp} identifiers to concrete,
+ * op-specialized SIMD processors. 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
{
@@ -98,4 +101,30 @@ public final class SimdProcessors
default -> throw DruidException.defensive("Unsupported SIMD binary
op[%s]", op);
};
}
+
+ public static ExprVectorProcessor<long[]> makeLongUnary(
+ ExprVectorProcessor<?> input,
+ SimdSupportedUnaryOp op,
+ LongUnivariateLongFunction scalarFallback
+ )
+ {
+ return switch (op) {
+ case NEG -> new SimdLongNegProcessor(input, scalarFallback);
+ case ABS -> new SimdLongAbsProcessor(input, scalarFallback);
+ default -> throw DruidException.defensive("Unsupported SIMD unary
op[%s]", op);
+ };
+ }
+
+ public static ExprVectorProcessor<double[]> makeDoubleUnary(
+ ExprVectorProcessor<?> input,
+ SimdSupportedUnaryOp op,
+ DoubleUnivariateDoubleFunction scalarFallback
+ )
+ {
+ return switch (op) {
+ case NEG -> new SimdDoubleNegProcessor(input, scalarFallback);
+ case ABS -> new SimdDoubleAbsProcessor(input, scalarFallback);
+ default -> throw DruidException.defensive("Unsupported SIMD unary
op[%s]", op);
+ };
+ }
}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdSupportedUnaryOp.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdSupportedUnaryOp.java
new file mode 100644
index 00000000000..260fd24d2ac
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdSupportedUnaryOp.java
@@ -0,0 +1,35 @@
+/*
+ * 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 unary math operations have a {@code jdk.incubator.vector}
(SIMD) specialization. Used by
+ * {@link
org.apache.druid.math.expr.vector.SimpleVectorMathUnivariateProcessorFactory}
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 SimdSupportedUnaryOp
+{
+ NEG,
+ ABS
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]