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 87943a1ebfd feat: simd division expression (#19686)
87943a1ebfd is described below
commit 87943a1ebfd6013f51cc56aee0bf9dc40217e39f
Author: Clint Wylie <[email protected]>
AuthorDate: Wed Jul 15 14:51:28 2026 -0700
feat: simd division expression (#19686)
---
.../benchmark/query/SqlExpressionBenchmark.java | 90 ++++++++++----------
.../SimpleVectorMathBivariateProcessorFactory.java | 2 +-
.../math/expr/vector/VectorMathProcessors.java | 3 +-
.../vector/simd/SimdDoubleDoubleDivProcessor.java | 95 +++++++++++++++++++++
.../vector/simd/SimdDoubleLongDivProcessor.java | 96 ++++++++++++++++++++++
.../vector/simd/SimdLongDoubleDivProcessor.java | 96 ++++++++++++++++++++++
.../math/expr/vector/simd/SimdProcessors.java | 3 +
.../expr/vector/simd/SimdSupportedBinaryOp.java | 28 ++++++-
8 files changed, 362 insertions(+), 51 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 28a3ac8672a..5bc0019e723 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
@@ -63,87 +63,91 @@ public class SqlExpressionBenchmark extends
SqlBaseQueryBenchmark
"SELECT string2, SUM(long1) FROM expressions GROUP BY 1 ORDER BY 2",
// 6: group by non-expr with 2 agg
"SELECT string2, SUM(long1), SUM(double3) FROM expressions GROUP BY 1
ORDER BY 2",
+ // 7: min/max on a nullable long column (exercises the null-aware SIMD
path)
+ "SELECT MIN(long5), MAX(long5) FROM expressions",
+ // 8: min/max across long/double/float, non-null columns
+ "SELECT MIN(long1), MAX(long1), MIN(double1), MAX(double1), MIN(float3),
MAX(float3) FROM expressions",
// ===========================
// expressions
// ===========================
- // 7: math op - 2 longs
+ // 9: math op - 2 longs
"SELECT SUM(long1 * long2) FROM expressions",
- // 8: mixed math - 2 longs, 1 double
+ // 10: mixed math - 2 longs, 1 double
"SELECT SUM((long1 * long2) / double1) FROM expressions",
- // 9: mixed math - 2 longs, 1 double, 1 float
+ // 11: mixed math - 2 longs, 1 double, 1 float
"SELECT SUM(float3 + ((long1 * long4)/double1)) FROM expressions",
- // 10: mixed math - 3 longs, 1 double, 1 float
+ // 12: mixed math - 3 longs, 1 double, 1 float
"SELECT SUM(long5 - (float3 + ((long1 * long4)/double1))) FROM
expressions",
- // 11: all same math op - 3 longs, 1 double, 1 float
+ // 13: all same math op - 3 longs, 1 double, 1 float
"SELECT SUM(long5 * float3 * long1 * long4 * double1) FROM expressions",
- // 12: cos
+ // 14: cos
"SELECT cos(double2) FROM expressions",
- // 13: unary negate
+ // 15: unary negate
"SELECT SUM(-long4) FROM expressions",
- // 14: string long
+ // 16: string long
"SELECT SUM(PARSE_LONG(string1)) FROM expressions",
- // 15: string longer
+ // 17: string longer
"SELECT SUM(PARSE_LONG(string3)) FROM expressions",
- // 16: time floor, non-expr col + reg agg
+ // 18: time floor, non-expr col + reg agg
"SELECT TIME_FLOOR(__time, 'PT1H'), string2, SUM(double4) FROM
expressions GROUP BY 1,2 ORDER BY 3",
- // 17: time floor, non-expr col + expr agg
+ // 19: time floor, non-expr col + expr agg
"SELECT TIME_FLOOR(__time, 'PT1H'), string2, SUM(long1 * double4) FROM
expressions GROUP BY 1,2 ORDER BY 3",
- // 18: time floor + non-expr agg (timeseries) (non-expression reference)
+ // 20: time floor + non-expr agg (timeseries) (non-expression reference)
"SELECT TIME_FLOOR(__time, 'PT1H'), SUM(long1) FROM expressions GROUP BY
1 ORDER BY 1",
- // 19: time floor + expr agg (timeseries)
+ // 21: time floor + expr agg (timeseries)
"SELECT TIME_FLOOR(__time, 'PT1H'), SUM(long1 * long4) FROM expressions
GROUP BY 1 ORDER BY 1",
- // 20: time floor + non-expr agg (group by)
+ // 22: time floor + non-expr agg (group by)
"SELECT TIME_FLOOR(__time, 'PT1H'), SUM(long1) FROM expressions GROUP BY
1 ORDER BY 2",
- // 21: time floor + expr agg (group by)
+ // 23: time floor + expr agg (group by)
"SELECT TIME_FLOOR(__time, 'PT1H'), SUM(long1 * long4) FROM expressions
GROUP BY 1 ORDER BY 2",
- // 22: time floor offset by 1 day + non-expr agg (group by)
+ // 24: time floor offset by 1 day + non-expr agg (group by)
"SELECT TIME_FLOOR(TIMESTAMPADD(DAY, -1, __time), 'PT1H'), SUM(long1)
FROM expressions GROUP BY 1 ORDER BY 1",
- // 23: time floor offset by 1 day + expr agg (group by)
+ // 25: time floor offset by 1 day + expr agg (group by)
"SELECT TIME_FLOOR(TIMESTAMPADD(DAY, -1, __time), 'PT1H'), SUM(long1 *
long4) FROM expressions GROUP BY 1 ORDER BY 1",
- // 24: group by long expr with non-expr agg
+ // 26: group by long expr with non-expr agg
"SELECT (long1 * long2), SUM(double1) FROM expressions GROUP BY 1 ORDER
BY 2",
- // 25: group by non-expr with expr agg
+ // 27: group by non-expr with expr agg
"SELECT string2, SUM(long1 * long4) FROM expressions GROUP BY 1 ORDER BY
2",
- // 26: group by string expr with non-expr agg
+ // 28: group by string expr with non-expr agg
"SELECT CONCAT(string2, '-', long2), SUM(double1) FROM expressions GROUP
BY 1 ORDER BY 2",
- // 27: group by string expr with expr agg
+ // 29: group by string expr with expr agg
"SELECT CONCAT(string2, '-', long2), SUM(long1 * double4) FROM
expressions GROUP BY 1 ORDER BY 2",
- // 28: group by single input string low cardinality expr with expr agg
+ // 30: group by single input string low cardinality expr with expr agg
"SELECT CONCAT(string2, '-', 'expressions'), SUM(long1 * long4) FROM
expressions GROUP BY 1 ORDER BY 2",
- // 29: group by single input string high cardinality expr with expr agg
+ // 31: group by single input string high cardinality expr with expr agg
"SELECT CONCAT(string3, '-', 'expressions'), SUM(long1 * long4) FROM
expressions GROUP BY 1 ORDER BY 2",
- // 30: logical and operator
+ // 32: logical and operator
"SELECT CAST(long1 as BOOLEAN) AND CAST (long2 as BOOLEAN), COUNT(*)
FROM expressions GROUP BY 1 ORDER BY 2",
- // 31: isnull, notnull
+ // 33: isnull, notnull
"SELECT long5 IS NULL, long3 IS NOT NULL, count(*) FROM expressions
GROUP BY 1,2 ORDER BY 3",
- // 32: time shift, non-expr col + reg agg, regular
+ // 34: time shift, non-expr col + reg agg, regular
"SELECT TIME_SHIFT(__time, 'PT1H', 3), string2, SUM(double4) FROM
expressions GROUP BY 1,2 ORDER BY 3",
- // 33: time shift, non-expr col + expr agg, sequential low cardinality
+ // 35: time shift, non-expr col + expr agg, sequential low cardinality
"SELECT TIME_SHIFT(MILLIS_TO_TIMESTAMP(long1), 'PT1H', 1), string2,
SUM(long1 * double4) FROM expressions GROUP BY 1,2 ORDER BY 3",
- // 34: time shift + non-expr agg (timeseries) (non-expression
reference), zipf distribution low cardinality
+ // 36: time shift + non-expr agg (timeseries) (non-expression
reference), zipf distribution low cardinality
"SELECT TIME_SHIFT(MILLIS_TO_TIMESTAMP(long2), 'PT1H', 1), string2,
SUM(long1 * double4) FROM expressions GROUP BY 1,2 ORDER BY 3",
- // 35: time shift + expr agg (timeseries), zipf distribution high
cardinality
+ // 37: time shift + expr agg (timeseries), zipf distribution high
cardinality
"SELECT TIME_SHIFT(MILLIS_TO_TIMESTAMP(long3), 'PT1H', 1), string2,
SUM(long1 * double4) FROM expressions GROUP BY 1,2 ORDER BY 3",
- // 36: time shift + non-expr agg (group by), uniform distribution low
cardinality
+ // 38: time shift + non-expr agg (group by), uniform distribution low
cardinality
"SELECT TIME_SHIFT(MILLIS_TO_TIMESTAMP(long4), 'PT1H', 1), string2,
SUM(long1 * double4) FROM expressions GROUP BY 1,2 ORDER BY 3",
- // 37: time shift + expr agg (group by), uniform distribution high
cardinality
+ // 39: time shift + expr agg (group by), uniform distribution high
cardinality
"SELECT TIME_SHIFT(MILLIS_TO_TIMESTAMP(long5), 'PT1H', 1), string2,
SUM(long1 * double4) FROM expressions GROUP BY 1,2 ORDER BY 3",
- // 38,39: array element filtering
+ // 40,41: array element filtering
"SELECT string1, long1 FROM expressions WHERE
ARRAY_CONTAINS(\"multi-string3\", 100) GROUP BY 1,2",
"SELECT string1, long1 FROM expressions WHERE
ARRAY_OVERLAP(\"multi-string3\", ARRAY[100, 200]) GROUP BY 1,2",
- // 40: regex filtering
+ // 42: regex filtering
"SELECT string4, COUNT(*) FROM expressions WHERE REGEXP_EXTRACT(string1,
'^1') IS NOT NULL OR REGEXP_EXTRACT('Z' || string2, '^Z2') IS NOT NULL GROUP BY
1",
- // 41: complicated filtering
+ // 43: complicated filtering
"SELECT string2, SUM(long1) FROM expressions WHERE string1 = '1000' AND
string5 LIKE '%1%' AND (string3 in ('1', '10', '20', '22', '32') AND long2 IN
(1, 19, 21, 23, 25, 26, 46) AND double3 < 1010.0 AND double3 > 1000.0 AND
(string4 = '1' OR REGEXP_EXTRACT(string1, '^1') IS NOT NULL OR
REGEXP_EXTRACT('Z' || string2, '^Z2') IS NOT NULL)) GROUP BY 1 ORDER BY 2",
- // 42: array_contains expr
+ // 44-47: array_contains / array_overlap exprs
"SELECT ARRAY_CONTAINS(\"multi-string3\", 100) FROM expressions",
"SELECT ARRAY_CONTAINS(\"multi-string3\", ARRAY[1, 2, 10, 11, 20, 22,
30, 33, 40, 44, 50, 55, 100]) FROM expressions",
"SELECT ARRAY_OVERLAP(\"multi-string3\", ARRAY[1, 100]) FROM
expressions",
"SELECT ARRAY_OVERLAP(\"multi-string3\", ARRAY[1, 2, 10, 11, 20, 22, 30,
33, 40, 44, 50, 55, 100]) FROM expressions",
- // 46: filters with random orders
+ // 48,49: filters with random orders
"SELECT string2, SUM(long1) FROM expressions WHERE string5 LIKE '%1%'
AND string1 = '1000' GROUP BY 1 ORDER BY 2",
"SELECT string2, SUM(long1) FROM expressions WHERE string5 LIKE '%1%'
AND (string3 in ('1', '10', '20', '22', '32') AND long2 IN (1, 19, 21, 23, 25,
26, 46) AND double3 < 1010.0 AND double3 > 1000.0 AND (string4 = '1' OR
REGEXP_EXTRACT(string1, '^1') IS NOT NULL OR REGEXP_EXTRACT('Z' || string2,
'^Z2') IS NOT NULL)) AND string1 = '1000' GROUP BY 1 ORDER BY 2",
- // 48-57 nvl tests
+ // 50-59: nvl tests
// lower cardinality
"SELECT NVL(string2, string1), SUM(double1) FROM expressions GROUP BY 1
ORDER BY 2",
"SELECT NVL(string2, CONCAT(string1, '-', long2)), SUM(double1) FROM
expressions GROUP BY 1 ORDER BY 2",
@@ -159,18 +163,12 @@ public class SqlExpressionBenchmark extends
SqlBaseQueryBenchmark
// numeric no lhs null
"SELECT NVL(long1, long3), SUM(double1) FROM expressions GROUP BY 1
ORDER BY 2",
"SELECT NVL(long1, long5 + long3), SUM(double1) FROM expressions GROUP
BY 1 ORDER BY 2",
+ // 60: case
"SELECT CASE WHEN MOD(long1, 2) = 0 THEN -1 WHEN MOD(long1, 2) = 1 THEN
long2 / MOD(long1, 2) ELSE long3 END FROM expressions GROUP BY 1",
- // 59-61 cast
+ // 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 [...]
- // ===========================
- // more non-expression reference queries: min/max aggregators
- // ===========================
- // 62: min/max on a nullable long column (exercises the null-aware SIMD
path)
- "SELECT MIN(long5), MAX(long5) FROM expressions",
- // 63: min/max across long/double/float, non-null columns
- "SELECT MIN(long1), MAX(long1), MIN(double1), MAX(double1), MIN(float3),
MAX(float3) FROM expressions"
+ "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 [...]
);
@Param({
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 deebbeb565f..42bf3e930a8 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
@@ -78,7 +78,7 @@ public class SimpleVectorMathBivariateProcessorFactory
extends VectorMathBivaria
@Override
public final ExprVectorProcessor<long[]>
longsProcessor(Expr.VectorInputBindingInspector inspector, Expr left, Expr
right)
{
- if (simdOp != null && ExpressionProcessing.useVectorApi()) {
+ if (simdOp != null && simdOp.supportsLongLong() &&
ExpressionProcessing.useVectorApi()) {
return SimdProcessors.makeLongLong(
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 4a26f814153..3b4f3b8cb30 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
@@ -367,7 +367,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.DIV
);
}
}
diff --git
a/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleDivProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleDivProcessor.java
new file mode 100644
index 00000000000..9f939c4775c
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleDoubleDivProcessor.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.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[]} division.
The op is hardcoded to
+ * {@link DoubleVector#div} so the JIT statically resolves it to the
platform's double-divide intrinsic.
+ * Division by zero produces the same {@code +/-Infinity} / {@code NaN} values
as the scalar path, no
+ * exceptions are thrown for IEEE-754 double division.
+ */
+public final class SimdDoubleDoubleDivProcessor extends
SimdDoubleDoubleProcessor
+{
+ public SimdDoubleDoubleDivProcessor(
+ 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.div(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.div(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/SimdDoubleLongDivProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongDivProcessor.java
new file mode 100644
index 00000000000..7abff42d0a4
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdDoubleLongDivProcessor.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[]} division. The
right long lane is widened to
+ * {@link DoubleVector} via {@code castShape} before the hardcoded {@link
DoubleVector#div} call.
+ */
+public final class SimdDoubleLongDivProcessor extends SimdDoubleLongProcessor
+{
+ public SimdDoubleLongDivProcessor(
+ 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.div(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.div(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/SimdLongDoubleDivProcessor.java
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleDivProcessor.java
new file mode 100644
index 00000000000..bc4792036a6
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/math/expr/vector/simd/SimdLongDoubleDivProcessor.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[]} division. The
left long lane is widened to
+ * {@link DoubleVector} via {@code castShape} before the hardcoded {@link
DoubleVector#div} call.
+ */
+public final class SimdLongDoubleDivProcessor extends SimdLongDoubleProcessor
+{
+ public SimdLongDoubleDivProcessor(
+ 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.div(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.div(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
index d8d74021c7a..55ca4c81076 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
@@ -62,6 +62,7 @@ public final class SimdProcessors
case ADD -> new SimdDoubleDoubleAddProcessor(left, right,
scalarFallback);
case SUB -> new SimdDoubleDoubleSubProcessor(left, right,
scalarFallback);
case MUL -> new SimdDoubleDoubleMulProcessor(left, right,
scalarFallback);
+ case DIV -> new SimdDoubleDoubleDivProcessor(left, right,
scalarFallback);
default -> throw DruidException.defensive("Unsupported SIMD binary
op[%s]", op);
};
}
@@ -77,6 +78,7 @@ public final class SimdProcessors
case ADD -> new SimdLongDoubleAddProcessor(left, right, scalarFallback);
case SUB -> new SimdLongDoubleSubProcessor(left, right, scalarFallback);
case MUL -> new SimdLongDoubleMulProcessor(left, right, scalarFallback);
+ case DIV -> new SimdLongDoubleDivProcessor(left, right, scalarFallback);
default -> throw DruidException.defensive("Unsupported SIMD binary
op[%s]", op);
};
}
@@ -92,6 +94,7 @@ public final class SimdProcessors
case ADD -> new SimdDoubleLongAddProcessor(left, right, scalarFallback);
case SUB -> new SimdDoubleLongSubProcessor(left, right, scalarFallback);
case MUL -> new SimdDoubleLongMulProcessor(left, right, scalarFallback);
+ case DIV -> new SimdDoubleLongDivProcessor(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
index 953571ba4d3..1192d064214 100644
---
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
@@ -30,7 +30,29 @@ package org.apache.druid.math.expr.vector.simd;
*/
public enum SimdSupportedBinaryOp
{
- ADD,
- SUB,
- MUL
+ ADD(true),
+ SUB(true),
+ MUL(true),
+ /**
+ * {@code jdk.incubator.vector} has no long-integer division intrinsic (SIMD
hardware lacks it), and long
+ * division by zero would throw {@link ArithmeticException} in the middle of
a chunk. So the {@code long × long}
+ * combo falls through to the scalar processor for DIV; only the
double-output combos are SIMD-specialized.
+ */
+ DIV(false);
+
+ private final boolean supportsLongLong;
+
+ SimdSupportedBinaryOp(boolean supportsLongLong)
+ {
+ this.supportsLongLong = supportsLongLong;
+ }
+
+ /**
+ * Whether this op has a SIMD specialization for the {@code long × long ->
long} type combo. Callers can use
+ * this to decide whether to route the {@code longsProcessor} path to SIMD
or fall back to scalar.
+ */
+ public boolean supportsLongLong()
+ {
+ return supportsLongLong;
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]