This is an automated email from the ASF dual-hosted git repository.
gianm 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 d70bf3a5c4d feat: SIMD sum vector aggregators (Long/Double/Float)
(#19561)
d70bf3a5c4d is described below
commit d70bf3a5c4d08863eb9cb6e8789e56d9055986bc
Author: Clint Wylie <[email protected]>
AuthorDate: Mon Jul 6 10:48:24 2026 -0700
feat: SIMD sum vector aggregators (Long/Double/Float) (#19561)
---
.../benchmark/query/SqlExpressionBenchmark.java | 6 +-
docs/configuration/index.md | 12 +
pom.xml | 1 +
.../aggregation/DoubleSumAggregatorFactory.java | 5 +
.../aggregation/FloatSumAggregatorFactory.java | 5 +
.../aggregation/LongSumAggregatorFactory.java | 5 +
.../aggregation/NullAwareVectorAggregator.java | 46 +++
.../NullableNumericVectorAggregator.java | 13 +-
.../simd/SimdDoubleSumVectorAggregator.java | 98 ++++++
.../simd/SimdFloatSumVectorAggregator.java | 98 ++++++
.../simd/SimdLongSumVectorAggregator.java | 98 ++++++
.../simd/SimdSumVectorAggregatorTest.java | 392 +++++++++++++++++++++
.../query/groupby/GroupByQueryRunnerTest.java | 41 ++-
.../groupby/GroupByTimeseriesQueryRunnerTest.java | 18 +-
.../timeseries/TimeseriesQueryRunnerTest.java | 34 +-
website/.spelling | 1 +
16 files changed, 853 insertions(+), 20 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 0ef6395a1fc..1c2b3c723f5 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
@@ -49,10 +49,10 @@ public class SqlExpressionBenchmark extends
SqlBaseQueryBenchmark
// ===========================
// non-expression reference queries
// ===========================
- // 0: non-expression timeseries reference, 1 columns
+ // 0: non-expression timeseries reference, 1 column
"SELECT SUM(long1) FROM expressions",
- // 1: non-expression timeseries reference, 2 columns
- "SELECT SUM(long1), SUM(long2) FROM expressions",
+ // 1: non-expression timeseries reference, 1 column with nulls
+ "SELECT SUM(long5) FROM expressions",
// 2: non-expression timeseries reference, 3 columns
"SELECT SUM(long1), SUM(long4), SUM(double1) FROM expressions",
// 3: non-expression timeseries reference, 4 columns
diff --git a/docs/configuration/index.md b/docs/configuration/index.md
index f25e1bdd99f..92c2fc9103b 100644
--- a/docs/configuration/index.md
+++ b/docs/configuration/index.md
@@ -641,6 +641,18 @@ the following properties.
JavaScript-based functionality is disabled by default. Please refer to the
Druid [JavaScript programming guide](../development/javascript.md) for
guidelines about using Druid's JavaScript functionality, including instructions
on how to enable it.
:::
+### Expression processing
+
+The properties below tune Druid's native expression engine, which evaluates
virtual columns, expression-based filters,
+the `expression` aggregator/post-aggregator, and any SQL functions that lower
to native expressions.
+
+|Property|Description|Default|
+|--------|-----------|-------|
+|`druid.expressions.processArraysAsMultiValueStrings`|If true, all `ARRAY`
typed values are converted to `STRING` by column selectors and treated as
multi-value strings rather than native arrays. Provided for backwards
compatibility with the behavior of Druid 24.0 and earlier, before array types
were introduced.|false|
+|`druid.expressions.homogenizeNullMultiValueStringArrays`|If true, multi-value
string expression input values of `null`, `[]`, and `[null]` are all coerced to
`[null]`. Provided for backwards compatibility with Druid 0.22 and earlier. If
false (the default), this coercion only happens when single-value expressions
are implicitly mapped across multi-value rows, so the single-valued expression
is evaluated with an input of `null`.|false|
+|`druid.expressions.allowVectorizeFallback`|If true, the vectorized query
engine handles expressions without a native vectorized implementation using a
fallback processor that invokes the scalar expression evaluator in a loop. If
false, such expressions cannot be vectorized and the query falls back to the
non-vectorized engine.|true|
+|`druid.expressions.useVectorApi`|If true, vectorized expression vector
processors and numeric vector aggregators dispatch to SIMD specializations
backed by the JDK incubator Vector API (`jdk.incubator.vector`) where
available. Requires `--add-modules=jdk.incubator.vector` on the JVM command
line (see [strong encapsulation](../operations/java.md#strong-encapsulation)).
Off by default while the Vector API remains an incubator JDK feature.|false|
+
### Double column storage
Prior to version 0.13.0, Druid's storage layer used a 32-bit float
representation to store columns created by the
diff --git a/pom.xml b/pom.xml
index 36e8f2321bf..b8c72b78a03 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1794,6 +1794,7 @@
<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>
+ <exclude>**/query/aggregation/simd/Simd*.class</exclude>
</excludes>
<suppressAnnotations>
<annotation>**.SuppressForbidden</annotation>
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java
b/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java
index 6a433b7bf8f..dd67990fb37 100644
---
a/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Supplier;
import org.apache.druid.math.expr.ExprMacroTable;
+import org.apache.druid.math.expr.ExpressionProcessing;
+import org.apache.druid.query.aggregation.simd.SimdDoubleSumVectorAggregator;
import org.apache.druid.segment.BaseDoubleColumnValueSelector;
import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
import org.apache.druid.segment.vector.VectorValueSelector;
@@ -81,6 +83,9 @@ public class DoubleSumAggregatorFactory extends
SimpleDoubleAggregatorFactory
VectorValueSelector selector
)
{
+ if (ExpressionProcessing.useVectorApi()) {
+ return new SimdDoubleSumVectorAggregator(selector);
+ }
return new DoubleSumVectorAggregator(selector);
}
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java
b/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java
index 397b55abed7..bbb2ec52f93 100644
---
a/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Supplier;
import org.apache.druid.math.expr.ExprMacroTable;
+import org.apache.druid.math.expr.ExpressionProcessing;
+import org.apache.druid.query.aggregation.simd.SimdFloatSumVectorAggregator;
import org.apache.druid.segment.BaseFloatColumnValueSelector;
import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
import org.apache.druid.segment.vector.VectorValueSelector;
@@ -81,6 +83,9 @@ public class FloatSumAggregatorFactory extends
SimpleFloatAggregatorFactory
VectorValueSelector selector
)
{
+ if (ExpressionProcessing.useVectorApi()) {
+ return new SimdFloatSumVectorAggregator(selector);
+ }
return new FloatSumVectorAggregator(selector);
}
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java
b/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java
index 8e70fac2536..a070fb30d40 100644
---
a/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Supplier;
import org.apache.druid.math.expr.ExprMacroTable;
+import org.apache.druid.math.expr.ExpressionProcessing;
+import org.apache.druid.query.aggregation.simd.SimdLongSumVectorAggregator;
import org.apache.druid.segment.BaseLongColumnValueSelector;
import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
import org.apache.druid.segment.vector.VectorValueSelector;
@@ -106,6 +108,9 @@ public class LongSumAggregatorFactory extends
SimpleLongAggregatorFactory
VectorValueSelector selector
)
{
+ if (ExpressionProcessing.useVectorApi()) {
+ return new SimdLongSumVectorAggregator(selector);
+ }
return new LongSumVectorAggregator(selector);
}
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/NullAwareVectorAggregator.java
b/processing/src/main/java/org/apache/druid/query/aggregation/NullAwareVectorAggregator.java
new file mode 100644
index 00000000000..de1852a5c6d
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/NullAwareVectorAggregator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.query.aggregation;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Capability marker for {@link VectorAggregator}s that can efficiently
aggregate a contiguous row range while
+ * skipping null inputs themselves (such as via SIMD masking) rather than
relying on the
+ * {@link NullableNumericVectorAggregator} wrapper to filter rows into the
per-row scatter-gather
+ * {@link VectorAggregator#aggregate(ByteBuffer, int, int[], int[], int)
aggregate} variant.
+ *
+ * When the wrapper sees the input batch has nulls (i.e. {@code
selector.getNullVector() != null}) and the
+ * delegate is an instance of this interface, it routes the call here instead
of falling back to the
+ * scatter-gather path.
+ */
+public interface NullAwareVectorAggregator extends VectorAggregator
+{
+ /**
+ * Aggregate rows {@code [startRow, endRow)} into the slot at {@code
position}, skipping rows where
+ * {@code nullVector[i] == true}. Implementations should use {@link
jdk.incubator.vector.VectorMask} (or
+ * equivalent) to avoid per-row branching.
+ *
+ * @return {@code true} if at least one non-null row was aggregated; {@code
false} if every row in the range
+ * was null. {@link NullableNumericVectorAggregator} uses this to
set or leave the null-marker byte
+ * that precedes the delegate's state.
+ */
+ boolean aggregate(ByteBuffer buf, int position, int startRow, int endRow,
boolean[] nullVector);
+}
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericVectorAggregator.java
b/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericVectorAggregator.java
index 9a91176e2ab..f056dfed3c8 100644
---
a/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericVectorAggregator.java
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericVectorAggregator.java
@@ -76,7 +76,16 @@ public class NullableNumericVectorAggregator implements
VectorAggregator
public void aggregate(ByteBuffer buf, int position, int startRow, int endRow)
{
final boolean[] nullVector = selector.getNullVector();
- if (nullVector != null) {
+ if (nullVector == null) {
+ doAggregate(buf, position, startRow, endRow);
+ } else if (delegate instanceof NullAwareVectorAggregator nullAware) {
+ // Delegate handles null inputs itself (typically via SIMD masking); set
the null marker only if it
+ // reports at least one non-null row contributed.
+ if (nullAware.aggregate(buf, position + Byte.BYTES, startRow, endRow,
nullVector)) {
+ buf.put(position, TypeStrategies.IS_NOT_NULL_BYTE);
+ }
+ } else {
+ // Fallback: filter non-null rows and route through the scatter-gather
variant with a uniform position.
// Deferred initialization, since vAggregationPositions and
vAggregationRows are only needed if nulls
// actually occur.
if (vAggregationPositions == null) {
@@ -94,8 +103,6 @@ public class NullableNumericVectorAggregator implements
VectorAggregator
Arrays.fill(vAggregationPositions, 0, j, position);
doAggregate(buf, j, vAggregationPositions, vAggregationRows, 0);
- } else {
- doAggregate(buf, position, startRow, endRow);
}
}
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdDoubleSumVectorAggregator.java
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdDoubleSumVectorAggregator.java
new file mode 100644
index 00000000000..ab4a2afb0cc
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdDoubleSumVectorAggregator.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.query.aggregation.simd;
+
+import jdk.incubator.vector.DoubleVector;
+import jdk.incubator.vector.VectorMask;
+import jdk.incubator.vector.VectorOperators;
+import jdk.incubator.vector.VectorSpecies;
+import org.apache.druid.query.aggregation.DoubleSumVectorAggregator;
+import org.apache.druid.query.aggregation.NullAwareVectorAggregator;
+import org.apache.druid.segment.vector.VectorValueSelector;
+
+import java.nio.ByteBuffer;
+
+/**
+ * SIMD specialization of {@link DoubleSumVectorAggregator}'s ungrouped
contiguous-range aggregation. The hot loop
+ * issues a hardcoded {@link DoubleVector#add} and a {@code
reduceLanes(VectorOperators.ADD)} so the JIT emits the
+ * platform's double-add and double-add-reduce intrinsics. Null lanes are
skipped via {@link VectorMask}.
+ *
+ * The grouped scatter-gather variant is inherited from the parent (scalar)
class.
+ */
+public final class SimdDoubleSumVectorAggregator extends
DoubleSumVectorAggregator implements NullAwareVectorAggregator
+{
+ private static final VectorSpecies<Double> SPECIES =
DoubleVector.SPECIES_PREFERRED;
+
+ private final VectorValueSelector selector;
+
+ public SimdDoubleSumVectorAggregator(VectorValueSelector selector)
+ {
+ super(selector);
+ this.selector = selector;
+ }
+
+ @Override
+ public void aggregate(ByteBuffer buf, int position, int startRow, int endRow)
+ {
+ final double[] vector = selector.getDoubleVector();
+
+ final int laneCount = SPECIES.length();
+ final int upperBound = startRow + SPECIES.loopBound(endRow - startRow);
+ int i = startRow;
+ DoubleVector vacc = DoubleVector.zero(SPECIES);
+ for (; i < upperBound; i += laneCount) {
+ vacc = vacc.add(DoubleVector.fromArray(SPECIES, vector, i));
+ }
+ double sum = vacc.reduceLanes(VectorOperators.ADD);
+ for (; i < endRow; i++) {
+ sum += vector[i];
+ }
+ buf.putDouble(position, buf.getDouble(position) + sum);
+ }
+
+ @Override
+ public boolean aggregate(ByteBuffer buf, int position, int startRow, int
endRow, boolean[] nullVector)
+ {
+ final double[] vector = selector.getDoubleVector();
+
+ final int laneCount = SPECIES.length();
+ final int upperBound = startRow + SPECIES.loopBound(endRow - startRow);
+ int i = startRow;
+ DoubleVector vacc = DoubleVector.zero(SPECIES);
+ int nonNullCount = 0;
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Double> notNull = VectorMask.fromArray(SPECIES,
nullVector, i).not();
+ vacc = vacc.add(DoubleVector.fromArray(SPECIES, vector, i), notNull);
+ nonNullCount += notNull.trueCount();
+ }
+ double sum = vacc.reduceLanes(VectorOperators.ADD);
+ for (; i < endRow; i++) {
+ if (!nullVector[i]) {
+ sum += vector[i];
+ nonNullCount++;
+ }
+ }
+ if (nonNullCount > 0) {
+ buf.putDouble(position, buf.getDouble(position) + sum);
+ return true;
+ }
+ return false;
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdFloatSumVectorAggregator.java
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdFloatSumVectorAggregator.java
new file mode 100644
index 00000000000..c9b843ca3ed
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdFloatSumVectorAggregator.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.query.aggregation.simd;
+
+import jdk.incubator.vector.FloatVector;
+import jdk.incubator.vector.VectorMask;
+import jdk.incubator.vector.VectorOperators;
+import jdk.incubator.vector.VectorSpecies;
+import org.apache.druid.query.aggregation.FloatSumVectorAggregator;
+import org.apache.druid.query.aggregation.NullAwareVectorAggregator;
+import org.apache.druid.segment.vector.VectorValueSelector;
+
+import java.nio.ByteBuffer;
+
+/**
+ * SIMD specialization of {@link FloatSumVectorAggregator}'s ungrouped
contiguous-range aggregation. The hot loop
+ * issues a hardcoded {@link FloatVector#add} and a {@code
reduceLanes(VectorOperators.ADD)} so the JIT emits the
+ * platform's float-add and float-add-reduce intrinsics. Null lanes are
skipped via {@link VectorMask}.
+ *
+ * The grouped scatter-gather variant is inherited from the parent (scalar)
class.
+ */
+public final class SimdFloatSumVectorAggregator extends
FloatSumVectorAggregator implements NullAwareVectorAggregator
+{
+ private static final VectorSpecies<Float> SPECIES =
FloatVector.SPECIES_PREFERRED;
+
+ private final VectorValueSelector selector;
+
+ public SimdFloatSumVectorAggregator(VectorValueSelector selector)
+ {
+ super(selector);
+ this.selector = selector;
+ }
+
+ @Override
+ public void aggregate(ByteBuffer buf, int position, int startRow, int endRow)
+ {
+ final float[] vector = selector.getFloatVector();
+
+ final int laneCount = SPECIES.length();
+ final int upperBound = startRow + SPECIES.loopBound(endRow - startRow);
+ int i = startRow;
+ FloatVector vacc = FloatVector.zero(SPECIES);
+ for (; i < upperBound; i += laneCount) {
+ vacc = vacc.add(FloatVector.fromArray(SPECIES, vector, i));
+ }
+ float sum = vacc.reduceLanes(VectorOperators.ADD);
+ for (; i < endRow; i++) {
+ sum += vector[i];
+ }
+ buf.putFloat(position, buf.getFloat(position) + sum);
+ }
+
+ @Override
+ public boolean aggregate(ByteBuffer buf, int position, int startRow, int
endRow, boolean[] nullVector)
+ {
+ final float[] vector = selector.getFloatVector();
+
+ final int laneCount = SPECIES.length();
+ final int upperBound = startRow + SPECIES.loopBound(endRow - startRow);
+ int i = startRow;
+ FloatVector vacc = FloatVector.zero(SPECIES);
+ int nonNullCount = 0;
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Float> notNull = VectorMask.fromArray(SPECIES,
nullVector, i).not();
+ vacc = vacc.add(FloatVector.fromArray(SPECIES, vector, i), notNull);
+ nonNullCount += notNull.trueCount();
+ }
+ float sum = vacc.reduceLanes(VectorOperators.ADD);
+ for (; i < endRow; i++) {
+ if (!nullVector[i]) {
+ sum += vector[i];
+ nonNullCount++;
+ }
+ }
+ if (nonNullCount > 0) {
+ buf.putFloat(position, buf.getFloat(position) + sum);
+ return true;
+ }
+ return false;
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdLongSumVectorAggregator.java
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdLongSumVectorAggregator.java
new file mode 100644
index 00000000000..a16355618e4
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdLongSumVectorAggregator.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.query.aggregation.simd;
+
+import jdk.incubator.vector.LongVector;
+import jdk.incubator.vector.VectorMask;
+import jdk.incubator.vector.VectorOperators;
+import jdk.incubator.vector.VectorSpecies;
+import org.apache.druid.query.aggregation.LongSumVectorAggregator;
+import org.apache.druid.query.aggregation.NullAwareVectorAggregator;
+import org.apache.druid.segment.vector.VectorValueSelector;
+
+import java.nio.ByteBuffer;
+
+/**
+ * SIMD specialization of {@link LongSumVectorAggregator}'s ungrouped
contiguous-range aggregation. The hot loop
+ * issues a hardcoded {@link LongVector#add} and a {@code
reduceLanes(VectorOperators.ADD)} so the JIT emits the
+ * platform's long-add and long-add-reduce intrinsics. Null lanes are skipped
via {@link VectorMask}.
+ *
+ * The grouped scatter-gather variant is inherited from the parent (scalar)
class.
+ */
+public final class SimdLongSumVectorAggregator extends LongSumVectorAggregator
implements NullAwareVectorAggregator
+{
+ private static final VectorSpecies<Long> SPECIES =
LongVector.SPECIES_PREFERRED;
+
+ private final VectorValueSelector selector;
+
+ public SimdLongSumVectorAggregator(VectorValueSelector selector)
+ {
+ super(selector);
+ this.selector = selector;
+ }
+
+ @Override
+ public void aggregate(ByteBuffer buf, int position, int startRow, int endRow)
+ {
+ final long[] vector = selector.getLongVector();
+
+ final int laneCount = SPECIES.length();
+ final int upperBound = startRow + SPECIES.loopBound(endRow - startRow);
+ int i = startRow;
+ LongVector vacc = LongVector.zero(SPECIES);
+ for (; i < upperBound; i += laneCount) {
+ vacc = vacc.add(LongVector.fromArray(SPECIES, vector, i));
+ }
+ long sum = vacc.reduceLanes(VectorOperators.ADD);
+ for (; i < endRow; i++) {
+ sum += vector[i];
+ }
+ buf.putLong(position, buf.getLong(position) + sum);
+ }
+
+ @Override
+ public boolean aggregate(ByteBuffer buf, int position, int startRow, int
endRow, boolean[] nullVector)
+ {
+ final long[] vector = selector.getLongVector();
+
+ final int laneCount = SPECIES.length();
+ final int upperBound = startRow + SPECIES.loopBound(endRow - startRow);
+ int i = startRow;
+ LongVector vacc = LongVector.zero(SPECIES);
+ int nonNullCount = 0;
+ for (; i < upperBound; i += laneCount) {
+ final VectorMask<Long> notNull = VectorMask.fromArray(SPECIES,
nullVector, i).not();
+ vacc = vacc.add(LongVector.fromArray(SPECIES, vector, i), notNull);
+ nonNullCount += notNull.trueCount();
+ }
+ long sum = vacc.reduceLanes(VectorOperators.ADD);
+ for (; i < endRow; i++) {
+ if (!nullVector[i]) {
+ sum += vector[i];
+ nonNullCount++;
+ }
+ }
+ if (nonNullCount > 0) {
+ buf.putLong(position, buf.getLong(position) + sum);
+ return true;
+ }
+ return false;
+ }
+}
diff --git
a/processing/src/test/java/org/apache/druid/query/aggregation/simd/SimdSumVectorAggregatorTest.java
b/processing/src/test/java/org/apache/druid/query/aggregation/simd/SimdSumVectorAggregatorTest.java
new file mode 100644
index 00000000000..19e54b075a3
--- /dev/null
+++
b/processing/src/test/java/org/apache/druid/query/aggregation/simd/SimdSumVectorAggregatorTest.java
@@ -0,0 +1,392 @@
+/*
+ * 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.query.aggregation.simd;
+
+import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.query.aggregation.DoubleSumVectorAggregator;
+import org.apache.druid.query.aggregation.FloatSumVectorAggregator;
+import org.apache.druid.query.aggregation.LongSumVectorAggregator;
+import org.apache.druid.segment.vector.VectorValueSelector;
+import org.apache.druid.testing.InitializedNullHandlingTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.annotation.Nullable;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.function.IntPredicate;
+
+/**
+ * For each (sum type, vector size, null pattern) combination, drives the SIMD
and scalar sum vector aggregators
+ * directly and asserts equivalent buffer state. Two paths covered:
+ * - the ungrouped no-null path (SIMD subclass's overridden {@code
aggregate(buf, pos, start, end)} vs the scalar
+ * parent's implementation), and
+ * - the null-aware path on the SIMD class (the new {@code aggregate(buf,
pos, start, end, nullVector)} declared by
+ * {@link org.apache.druid.query.aggregation.NullAwareVectorAggregator}),
compared against a manually-computed
+ * reference sum.
+ *
+ * Each scenario is exercised twice: once with {@code (position=0, startRow=0,
endRow=size)} and once with
+ * {@code (position=1, startRow=1, endRow=size+1)} where the row at index 0 is
a deliberately extreme "poison"
+ * value that would visibly skew the result if the aggregator incorrectly read
past {@code startRow}, and the
+ * buffer slot starts at byte offset 1 so any indexing off the position
parameter shows up.
+ */
+public class SimdSumVectorAggregatorTest extends InitializedNullHandlingTest
+{
+ private static final int[] VECTOR_SIZES = {1, 8, 17, 64, 1023};
+ private static final long POISON_LONG = Long.MAX_VALUE / 2;
+ private static final double POISON_DOUBLE = 1e15;
+ private static final float POISON_FLOAT = 1e10f;
+
+ @Test
+ public void testLongSum()
+ {
+ for (int size : VECTOR_SIZES) {
+ for (NullPattern pattern : NullPattern.values()) {
+ runLong(size, pattern, 0, 0);
+ runLong(size, pattern, 1, 1);
+ }
+ }
+ }
+
+ @Test
+ public void testDoubleSum()
+ {
+ for (int size : VECTOR_SIZES) {
+ for (NullPattern pattern : NullPattern.values()) {
+ runDouble(size, pattern, 0, 0);
+ runDouble(size, pattern, 1, 1);
+ }
+ }
+ }
+
+ @Test
+ public void testFloatSum()
+ {
+ for (int size : VECTOR_SIZES) {
+ for (NullPattern pattern : NullPattern.values()) {
+ runFloat(size, pattern, 0, 0);
+ runFloat(size, pattern, 1, 1);
+ }
+ }
+ }
+
+ private static void runLong(int size, NullPattern pattern, int position, int
startRow)
+ {
+ final int arrLen = startRow + size;
+ final long[] values = new long[arrLen];
+ for (int i = 0; i < startRow; i++) {
+ values[i] = POISON_LONG;
+ }
+ System.arraycopy(randomLongs(size, 0), 0, values, startRow, size);
+
+ final boolean[] realNulls = pattern.toMask(size);
+ final boolean[] nulls = realNulls == null ? null : padNulls(realNulls,
startRow);
+
+ final FakeVectorValueSelector selector = new
FakeVectorValueSelector(arrLen, values, null, null, nulls);
+ final int endRow = startRow + size;
+ final String msg = StringUtils.format(
+ "type[long] size[%s] nulls[%s] pos[%s] start[%s]",
+ size, pattern, position, startRow
+ );
+
+ final LongSumVectorAggregator scalar = new
LongSumVectorAggregator(selector);
+ final SimdLongSumVectorAggregator simd = new
SimdLongSumVectorAggregator(selector);
+
+ if (nulls == null) {
+ final ByteBuffer scalarBuf = ByteBuffer.allocate(position + Long.BYTES);
+ final ByteBuffer simdBuf = ByteBuffer.allocate(position + Long.BYTES);
+ scalar.init(scalarBuf, position);
+ simd.init(simdBuf, position);
+ scalar.aggregate(scalarBuf, position, startRow, endRow);
+ simd.aggregate(simdBuf, position, startRow, endRow);
+ Assert.assertEquals(msg, scalarBuf.getLong(position),
simdBuf.getLong(position));
+ } else {
+ long expected = 0;
+ boolean anyNonNull = false;
+ for (int i = startRow; i < endRow; i++) {
+ if (!nulls[i]) {
+ expected += values[i];
+ anyNonNull = true;
+ }
+ }
+ final ByteBuffer simdBuf = ByteBuffer.allocate(position + Long.BYTES);
+ simd.init(simdBuf, position);
+ final boolean reported = simd.aggregate(simdBuf, position, startRow,
endRow, nulls);
+ Assert.assertEquals(msg + " (anyNonNull)", anyNonNull, reported);
+ if (reported) {
+ Assert.assertEquals(msg, expected, simdBuf.getLong(position));
+ }
+ }
+ }
+
+ private static void runDouble(int size, NullPattern pattern, int position,
int startRow)
+ {
+ final int arrLen = startRow + size;
+ final double[] values = new double[arrLen];
+ for (int i = 0; i < startRow; i++) {
+ values[i] = POISON_DOUBLE;
+ }
+ System.arraycopy(randomDoubles(size, 1), 0, values, startRow, size);
+
+ final boolean[] realNulls = pattern.toMask(size);
+ final boolean[] nulls = realNulls == null ? null : padNulls(realNulls,
startRow);
+
+ final FakeVectorValueSelector selector = new
FakeVectorValueSelector(arrLen, null, values, null, nulls);
+ final int endRow = startRow + size;
+ final String msg = StringUtils.format(
+ "type[double] size[%s] nulls[%s] pos[%s] start[%s]",
+ size, pattern, position, startRow
+ );
+
+ final DoubleSumVectorAggregator scalar = new
DoubleSumVectorAggregator(selector);
+ final SimdDoubleSumVectorAggregator simd = new
SimdDoubleSumVectorAggregator(selector);
+
+ if (nulls == null) {
+ final ByteBuffer scalarBuf = ByteBuffer.allocate(position +
Double.BYTES);
+ final ByteBuffer simdBuf = ByteBuffer.allocate(position + Double.BYTES);
+ scalar.init(scalarBuf, position);
+ simd.init(simdBuf, position);
+ scalar.aggregate(scalarBuf, position, startRow, endRow);
+ simd.aggregate(simdBuf, position, startRow, endRow);
+ Assert.assertEquals(
+ msg,
+ scalarBuf.getDouble(position),
+ simdBuf.getDouble(position),
+ Math.max(Math.abs(scalarBuf.getDouble(position)) * 1e-12, 1e-12)
+ );
+ } else {
+ double expected = 0;
+ boolean anyNonNull = false;
+ for (int i = startRow; i < endRow; i++) {
+ if (!nulls[i]) {
+ expected += values[i];
+ anyNonNull = true;
+ }
+ }
+ final ByteBuffer simdBuf = ByteBuffer.allocate(position + Double.BYTES);
+ simd.init(simdBuf, position);
+ final boolean reported = simd.aggregate(simdBuf, position, startRow,
endRow, nulls);
+ Assert.assertEquals(msg + " (anyNonNull)", anyNonNull, reported);
+ if (reported) {
+ Assert.assertEquals(
+ msg,
+ expected,
+ simdBuf.getDouble(position),
+ Math.max(Math.abs(expected) * 1e-12, 1e-12)
+ );
+ }
+ }
+ }
+
+ private static void runFloat(int size, NullPattern pattern, int position,
int startRow)
+ {
+ final int arrLen = startRow + size;
+ final float[] values = new float[arrLen];
+ for (int i = 0; i < startRow; i++) {
+ values[i] = POISON_FLOAT;
+ }
+ System.arraycopy(randomFloats(size, 2), 0, values, startRow, size);
+
+ final boolean[] realNulls = pattern.toMask(size);
+ final boolean[] nulls = realNulls == null ? null : padNulls(realNulls,
startRow);
+
+ final FakeVectorValueSelector selector = new
FakeVectorValueSelector(arrLen, null, null, values, nulls);
+ final int endRow = startRow + size;
+ final String msg = StringUtils.format(
+ "type[float] size[%s] nulls[%s] pos[%s] start[%s]",
+ size, pattern, position, startRow
+ );
+
+ final FloatSumVectorAggregator scalar = new
FloatSumVectorAggregator(selector);
+ final SimdFloatSumVectorAggregator simd = new
SimdFloatSumVectorAggregator(selector);
+
+ if (nulls == null) {
+ final ByteBuffer scalarBuf = ByteBuffer.allocate(position + Float.BYTES);
+ final ByteBuffer simdBuf = ByteBuffer.allocate(position + Float.BYTES);
+ scalar.init(scalarBuf, position);
+ simd.init(simdBuf, position);
+ scalar.aggregate(scalarBuf, position, startRow, endRow);
+ simd.aggregate(simdBuf, position, startRow, endRow);
+ Assert.assertEquals(
+ msg,
+ scalarBuf.getFloat(position),
+ simdBuf.getFloat(position),
+ Math.max(Math.abs(scalarBuf.getFloat(position)) * 1e-5f, 1e-5f)
+ );
+ } else {
+ float expected = 0;
+ boolean anyNonNull = false;
+ for (int i = startRow; i < endRow; i++) {
+ if (!nulls[i]) {
+ expected += values[i];
+ anyNonNull = true;
+ }
+ }
+ final ByteBuffer simdBuf = ByteBuffer.allocate(position + Float.BYTES);
+ simd.init(simdBuf, position);
+ final boolean reported = simd.aggregate(simdBuf, position, startRow,
endRow, nulls);
+ Assert.assertEquals(msg + " (anyNonNull)", anyNonNull, reported);
+ if (reported) {
+ Assert.assertEquals(
+ msg,
+ expected,
+ simdBuf.getFloat(position),
+ Math.max(Math.abs(expected) * 1e-5f, 1e-5f)
+ );
+ }
+ }
+ }
+
+ private static boolean[] padNulls(boolean[] realNulls, int startRow)
+ {
+ final boolean[] padded = new boolean[startRow + realNulls.length];
+ System.arraycopy(realNulls, 0, padded, startRow, realNulls.length);
+ return padded;
+ }
+
+ private static long[] randomLongs(int size, int seed)
+ {
+ final Random r = new Random(0xC0FFEEL + seed);
+ final long[] out = new long[size];
+ for (int i = 0; i < size; i++) {
+ out[i] = r.nextInt() & 0xFFFFFL;
+ }
+ return out;
+ }
+
+ private static double[] randomDoubles(int size, int seed)
+ {
+ final Random r = new Random(0xC0FFEEL + seed);
+ final double[] out = new double[size];
+ for (int i = 0; i < size; i++) {
+ out[i] = (r.nextDouble() - 0.5) * 1000.0;
+ }
+ return out;
+ }
+
+ private static float[] randomFloats(int size, int seed)
+ {
+ final Random r = new Random(0xC0FFEEL + seed);
+ final float[] out = new float[size];
+ for (int i = 0; i < size; i++) {
+ out[i] = (r.nextFloat() - 0.5f) * 1000.0f;
+ }
+ return out;
+ }
+
+ private enum NullPattern
+ {
+ NONE(i -> false),
+ ALL(i -> true),
+ ALTERNATING(i -> (i & 1) == 0),
+ SPARSE(i -> i % 7 == 0),
+ FIRST_THREE(i -> i < 3),
+ CHUNK_BOUNDARY(i -> i == 7 || i == 8);
+
+ private final IntPredicate predicate;
+
+ NullPattern(IntPredicate predicate)
+ {
+ this.predicate = predicate;
+ }
+
+ @Nullable
+ boolean[] toMask(int size)
+ {
+ if (this == NONE) {
+ return null; // models a column with no null vector at all
+ }
+ final boolean[] mask = new boolean[size];
+ for (int i = 0; i < size; i++) {
+ mask[i] = predicate.test(i);
+ }
+ return mask;
+ }
+ }
+
+ /**
+ * Minimal in-memory {@link VectorValueSelector} backed by pre-built
primitive arrays for tests. Only the
+ * accessor for the type used by a given test is non-null.
+ */
+ private static final class FakeVectorValueSelector implements
VectorValueSelector
+ {
+ private final int size;
+ @Nullable
+ private final long[] longs;
+ @Nullable
+ private final double[] doubles;
+ @Nullable
+ private final float[] floats;
+ @Nullable
+ private final boolean[] nulls;
+
+ FakeVectorValueSelector(
+ int size,
+ @Nullable long[] longs,
+ @Nullable double[] doubles,
+ @Nullable float[] floats,
+ @Nullable boolean[] nulls
+ )
+ {
+ this.size = size;
+ this.longs = longs;
+ this.doubles = doubles;
+ this.floats = floats;
+ this.nulls = nulls;
+ }
+
+ @Override
+ public long[] getLongVector()
+ {
+ return longs;
+ }
+
+ @Override
+ public float[] getFloatVector()
+ {
+ return floats;
+ }
+
+ @Override
+ public double[] getDoubleVector()
+ {
+ return doubles;
+ }
+
+ @Nullable
+ @Override
+ public boolean[] getNullVector()
+ {
+ return nulls;
+ }
+
+ @Override
+ public int getMaxVectorSize()
+ {
+ return size;
+ }
+
+ @Override
+ public int getCurrentVectorSize()
+ {
+ return size;
+ }
+ }
+}
diff --git
a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java
b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java
index b0fc2c51583..cd1102a0330 100644
---
a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java
+++
b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java
@@ -49,6 +49,7 @@ import org.apache.druid.java.util.common.guava.Sequences;
import org.apache.druid.java.util.metrics.StubServiceEmitter;
import org.apache.druid.js.JavaScriptConfig;
import org.apache.druid.math.expr.ExprMacroTable;
+import org.apache.druid.math.expr.ExpressionProcessing;
import org.apache.druid.query.BySegmentResultValue;
import org.apache.druid.query.BySegmentResultValueClass;
import org.apache.druid.query.ChainedExecutionQueryRunner;
@@ -144,9 +145,11 @@ import org.hamcrest.Description;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Period;
+import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Assume;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
@@ -211,6 +214,7 @@ public class GroupByQueryRunnerTest extends
InitializedNullHandlingTest
private final GroupByQueryConfig config;
private final boolean vectorize;
private final GroupByStatsProvider statsProvider;
+ private final boolean useVectorApi;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -433,11 +437,20 @@ public class GroupByQueryRunnerTest extends
InitializedNullHandlingTest
final GroupByQueryRunnerFactory factory = makeQueryRunnerFactory(config,
BUFFER_POOLS, statsProvider);
for (QueryRunner<ResultRow> runner :
QueryRunnerTestHelper.makeQueryRunners(factory, true)) {
for (boolean vectorize : ImmutableList.of(false, true)) {
- final String testName = StringUtils.format("config=%s, runner=%s,
vectorize=%s", config, runner, vectorize);
+ for (boolean useVectorApi : ImmutableList.of(false, true)) {
+ if (!vectorize && useVectorApi) {
+ // SIMD path is reachable only when vectorization is on; skip
the redundant combo.
+ continue;
+ }
+ final String testName = StringUtils.format(
+ "config=%s, runner=%s, vectorize=%s, useVectorApi=%s",
+ config, runner, vectorize, useVectorApi
+ );
- // Add vectorization tests for any indexes that support it.
- if (!vectorize ||
(QueryRunnerTestHelper.isTestRunnerVectorizable(runner))) {
- constructors.add(new Object[]{testName, config, factory, runner,
vectorize, statsProvider});
+ // Add vectorization tests for any indexes that support it.
+ if (!vectorize ||
(QueryRunnerTestHelper.isTestRunnerVectorizable(runner))) {
+ constructors.add(new Object[]{testName, config, factory, runner,
vectorize, statsProvider, useVectorApi});
+ }
}
}
}
@@ -468,7 +481,8 @@ public class GroupByQueryRunnerTest extends
InitializedNullHandlingTest
GroupByQueryRunnerFactory factory,
TestQueryRunner runner,
boolean vectorize,
- GroupByStatsProvider statsProvider
+ GroupByStatsProvider statsProvider,
+ boolean useVectorApi
)
{
this.config = config;
@@ -477,6 +491,23 @@ public class GroupByQueryRunnerTest extends
InitializedNullHandlingTest
this.originalRunner = runner;
this.vectorize = vectorize;
this.statsProvider = statsProvider;
+ this.useVectorApi = useVectorApi;
+ }
+
+ @Before
+ public void initializeExpressionProcessing()
+ {
+ if (useVectorApi) {
+ ExpressionProcessing.initializeForVectorApiTests();
+ } else {
+ ExpressionProcessing.initializeForTests();
+ }
+ }
+
+ @After
+ public void resetExpressionProcessing()
+ {
+ ExpressionProcessing.initializeForTests();
}
@Test
diff --git
a/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java
b/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java
index 6b2a9cb5120..9b682ba87b9 100644
---
a/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java
+++
b/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java
@@ -90,7 +90,7 @@ public class GroupByTimeseriesQueryRunnerTest extends
TimeseriesQueryRunnerTest
}
@SuppressWarnings("unchecked")
- @Parameterized.Parameters(name = "{0}, vectorize = {1}")
+ @Parameterized.Parameters(name = "{0}, vectorize = {1}, useVectorApi = {2}")
public static Iterable<Object[]> constructorFeeder()
{
setUpClass();
@@ -183,9 +183,15 @@ public class GroupByTimeseriesQueryRunnerTest extends
TimeseriesQueryRunnerTest
};
for (boolean vectorize : ImmutableList.of(false, true)) {
- // Add vectorization tests for any indexes that support it.
- if (!vectorize ||
QueryRunnerTestHelper.isTestRunnerVectorizable(runner)) {
- constructors.add(new Object[]{modifiedRunner, vectorize});
+ for (boolean useVectorApi : ImmutableList.of(false, true)) {
+ if (!vectorize && useVectorApi) {
+ // SIMD path is reachable only when vectorization is on; skip the
redundant combo.
+ continue;
+ }
+ // Add vectorization tests for any indexes that support it.
+ if (!vectorize ||
QueryRunnerTestHelper.isTestRunnerVectorizable(runner)) {
+ constructors.add(new Object[]{modifiedRunner, vectorize,
useVectorApi});
+ }
}
}
}
@@ -193,9 +199,9 @@ public class GroupByTimeseriesQueryRunnerTest extends
TimeseriesQueryRunnerTest
return constructors;
}
- public GroupByTimeseriesQueryRunnerTest(QueryRunner runner, boolean
vectorize)
+ public GroupByTimeseriesQueryRunnerTest(QueryRunner runner, boolean
vectorize, boolean useVectorApi)
{
- super(runner, false, vectorize,
QueryRunnerTestHelper.COMMON_DOUBLE_AGGREGATORS);
+ super(runner, false, vectorize,
QueryRunnerTestHelper.COMMON_DOUBLE_AGGREGATORS, useVectorApi);
}
// GroupBy handles timestamps differently when granularity is ALL
diff --git
a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
index 52cc5b14966..27697e57221 100644
---
a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
+++
b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
@@ -80,7 +80,9 @@ import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.Period;
+import org.junit.After;
import org.junit.Assert;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -106,7 +108,7 @@ public class TimeseriesQueryRunnerTest extends
InitializedNullHandlingTest
@Rule
public ExpectedException expectedException = ExpectedException.none();
- @Parameterized.Parameters(name = "{0}:descending={1},vectorize={2}")
+ @Parameterized.Parameters(name =
"{0}:descending={1},vectorize={2},useVectorApi={4}")
public static Iterable<Object[]> constructorFeeder()
{
final Iterable<Object[]> baseConstructors =
QueryRunnerTestHelper.cartesian(
@@ -124,7 +126,9 @@ public class TimeseriesQueryRunnerTest extends
InitializedNullHandlingTest
// vectorize?
Arrays.asList(false, true),
// double vs. float
- Arrays.asList(QueryRunnerTestHelper.COMMON_DOUBLE_AGGREGATORS,
QueryRunnerTestHelper.COMMON_FLOAT_AGGREGATORS)
+ Arrays.asList(QueryRunnerTestHelper.COMMON_DOUBLE_AGGREGATORS,
QueryRunnerTestHelper.COMMON_FLOAT_AGGREGATORS),
+ // useVectorApi? (SIMD aggregators)
+ Arrays.asList(false, true)
);
// Add vectorization tests for any indexes that support it.
@@ -136,6 +140,11 @@ public class TimeseriesQueryRunnerTest extends
InitializedNullHandlingTest
QueryRunnerTestHelper.isTestRunnerVectorizable((QueryRunner)
constructor[0])
&& !(boolean) constructor[1] /* descending */;
final boolean vectorize = (boolean) constructor[2]; /* vectorize
*/
+ final boolean useVectorApi = (boolean) constructor[4]; /*
useVectorApi */
+ if (!vectorize && useVectorApi) {
+ // SIMD path is reachable only when vectorization is on; skip
the redundant combo.
+ return false;
+ }
return !vectorize || canVectorize;
}
)
@@ -154,18 +163,37 @@ public class TimeseriesQueryRunnerTest extends
InitializedNullHandlingTest
protected final boolean descending;
protected final boolean vectorize;
protected final List<AggregatorFactory> aggregatorFactoryList;
+ protected final boolean useVectorApi;
public TimeseriesQueryRunnerTest(
QueryRunner<Result<TimeseriesResultValue>> runner,
boolean descending,
boolean vectorize,
- List<AggregatorFactory> aggregatorFactoryList
+ List<AggregatorFactory> aggregatorFactoryList,
+ boolean useVectorApi
)
{
this.runner = runner;
this.descending = descending;
this.vectorize = vectorize;
this.aggregatorFactoryList = aggregatorFactoryList;
+ this.useVectorApi = useVectorApi;
+ }
+
+ @Before
+ public void initializeExpressionProcessing()
+ {
+ if (useVectorApi) {
+ ExpressionProcessing.initializeForVectorApiTests();
+ } else {
+ ExpressionProcessing.initializeForTests();
+ }
+ }
+
+ @After
+ public void resetExpressionProcessing()
+ {
+ ExpressionProcessing.initializeForTests();
}
@Test
diff --git a/website/.spelling b/website/.spelling
index 7452721a172..468df62684f 100644
--- a/website/.spelling
+++ b/website/.spelling
@@ -238,6 +238,7 @@ S3
SAS
SDK
SIGAR
+SIMD
SNI
SPNEGO
Splunk
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]