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]

Reply via email to