This is an automated email from the ASF dual-hosted git repository.
jtuglu1 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 5b4755e942b perf: add SIMD acceleration for doubleMean aggregator
(#19681)
5b4755e942b is described below
commit 5b4755e942b079b86421d639508593bd81f499e7
Author: jtuglu1 <[email protected]>
AuthorDate: Tue Jul 14 22:31:42 2026 -0700
perf: add SIMD acceleration for doubleMean aggregator (#19681)
Adds SIMD acceleration for the doubleMean aggregator. Opt-in via the
useVectorApi flag.
---
.../DoubleMeanVectorAggregatorBenchmark.java | 179 +++++++++++++++++++++
.../mean/DoubleMeanAggregatorFactory.java | 9 +-
.../query/aggregation/mean/DoubleMeanHolder.java | 6 +
.../simd/SimdDoubleMeanVectorAggregator.java | 114 +++++++++++++
.../mean/DoubleMeanAggregationTest.java | 59 +++++--
.../simd/SimdDoubleMeanVectorAggregatorTest.java | 136 ++++++++++++++++
6 files changed, 491 insertions(+), 12 deletions(-)
diff --git
a/benchmarks/src/test/java/org/apache/druid/benchmark/DoubleMeanVectorAggregatorBenchmark.java
b/benchmarks/src/test/java/org/apache/druid/benchmark/DoubleMeanVectorAggregatorBenchmark.java
new file mode 100644
index 00000000000..65000ee2ea2
--- /dev/null
+++
b/benchmarks/src/test/java/org/apache/druid/benchmark/DoubleMeanVectorAggregatorBenchmark.java
@@ -0,0 +1,179 @@
+/*
+ * 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.benchmark;
+
+import org.apache.druid.query.aggregation.VectorAggregator;
+import org.apache.druid.query.aggregation.mean.DoubleMeanHolder;
+import org.apache.druid.query.aggregation.mean.DoubleMeanVectorAggregator;
+import org.apache.druid.query.aggregation.simd.SimdDoubleMeanVectorAggregator;
+import org.apache.druid.segment.vector.VectorValueSelector;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+
+import javax.annotation.Nullable;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+@State(Scope.Benchmark)
+@Fork(value = 1, jvmArgsAppend = "--add-modules=jdk.incubator.vector")
+@Warmup(iterations = 5)
+@Measurement(iterations = 5)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+public class DoubleMeanVectorAggregatorBenchmark
+{
+ @Param({"128", "512", "1024", "4096"})
+ private int vectorSize;
+
+ @Param({"none", "sparse", "alternating"})
+ private String nullPattern;
+
+ private ByteBuffer scalarBuffer;
+ private ByteBuffer simdBuffer;
+ private VectorAggregator scalarAggregator;
+ private VectorAggregator simdAggregator;
+
+ @Setup(Level.Trial)
+ public void setup()
+ {
+ final double[] values = new double[vectorSize];
+ final Random random = new Random(0xC0FFEEL);
+ for (int i = 0; i < vectorSize; i++) {
+ values[i] = (random.nextDouble() - 0.5) * 1000.0;
+ }
+
+ final boolean[] nulls = makeNullVector();
+ final VectorValueSelector selector = new
FakeVectorValueSelector(vectorSize, values, nulls);
+ scalarAggregator = new DoubleMeanVectorAggregator(selector);
+ simdAggregator = new SimdDoubleMeanVectorAggregator(selector);
+ scalarBuffer = ByteBuffer.allocate(DoubleMeanHolder.MAX_INTERMEDIATE_SIZE);
+ simdBuffer = ByteBuffer.allocate(DoubleMeanHolder.MAX_INTERMEDIATE_SIZE);
+ }
+
+ @Setup(Level.Invocation)
+ public void setupInvocation()
+ {
+ scalarAggregator.init(scalarBuffer, 0);
+ simdAggregator.init(simdBuffer, 0);
+ }
+
+ @Benchmark
+ public void scalarDoubleMean(final Blackhole blackhole)
+ {
+ scalarAggregator.aggregate(scalarBuffer, 0, 0, vectorSize);
+ blackhole.consume(scalarBuffer.getDouble(0));
+ blackhole.consume(scalarBuffer.getLong(Double.BYTES));
+ }
+
+ @Benchmark
+ public void simdDoubleMean(final Blackhole blackhole)
+ {
+ simdAggregator.aggregate(simdBuffer, 0, 0, vectorSize);
+ blackhole.consume(simdBuffer.getDouble(0));
+ blackhole.consume(simdBuffer.getLong(Double.BYTES));
+ }
+
+ @Nullable
+ private boolean[] makeNullVector()
+ {
+ return switch (nullPattern) {
+ case "none" -> null;
+ case "sparse" -> {
+ final boolean[] nulls = new boolean[vectorSize];
+ for (int i = 0; i < vectorSize; i++) {
+ nulls[i] = i % 17 == 0;
+ }
+ yield nulls;
+ }
+ case "alternating" -> {
+ final boolean[] nulls = new boolean[vectorSize];
+ for (int i = 0; i < vectorSize; i++) {
+ nulls[i] = (i & 1) == 0;
+ }
+ yield nulls;
+ }
+ default -> throw new IllegalStateException("Unsupported null pattern[" +
nullPattern + "]");
+ };
+ }
+
+ private static final class FakeVectorValueSelector implements
VectorValueSelector
+ {
+ private final int size;
+ private final double[] doubles;
+ @Nullable
+ private final boolean[] nulls;
+
+ FakeVectorValueSelector(final int size, final double[] doubles, @Nullable
final boolean[] nulls)
+ {
+ this.size = size;
+ this.doubles = doubles;
+ this.nulls = nulls;
+ }
+
+ @Override
+ public long[] getLongVector()
+ {
+ return null;
+ }
+
+ @Override
+ public float[] getFloatVector()
+ {
+ return null;
+ }
+
+ @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/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java
b/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java
index 58cbd502fef..eb81d496c20 100644
---
a/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java
@@ -25,17 +25,20 @@ import com.google.common.base.Preconditions;
import org.apache.druid.java.util.common.IAE;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.collect.Utils;
+import org.apache.druid.math.expr.ExpressionProcessing;
import org.apache.druid.query.aggregation.Aggregator;
import org.apache.druid.query.aggregation.AggregatorFactory;
import org.apache.druid.query.aggregation.AggregatorUtil;
import org.apache.druid.query.aggregation.BufferAggregator;
import org.apache.druid.query.aggregation.VectorAggregator;
+import org.apache.druid.query.aggregation.simd.SimdDoubleMeanVectorAggregator;
import org.apache.druid.query.cache.CacheKeyBuilder;
import org.apache.druid.segment.ColumnInspector;
import org.apache.druid.segment.ColumnSelectorFactory;
import org.apache.druid.segment.column.ColumnCapabilities;
import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
+import org.apache.druid.segment.vector.VectorValueSelector;
import javax.annotation.Nullable;
import java.util.Collections;
@@ -122,7 +125,11 @@ public class DoubleMeanAggregatorFactory extends
AggregatorFactory
@Override
public VectorAggregator factorizeVector(final VectorColumnSelectorFactory
selectorFactory)
{
- return new
DoubleMeanVectorAggregator(selectorFactory.makeValueSelector(fieldName));
+ final VectorValueSelector selector =
selectorFactory.makeValueSelector(fieldName);
+ if (ExpressionProcessing.useVectorApi()) {
+ return new SimdDoubleMeanVectorAggregator(selector);
+ }
+ return new DoubleMeanVectorAggregator(selector);
}
@Override
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanHolder.java
b/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanHolder.java
index f04150fcede..5b3d191cc6b 100644
---
a/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanHolder.java
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanHolder.java
@@ -107,6 +107,12 @@ public class DoubleMeanHolder
writeCount(buf, position, getCount(buf, position) + 1);
}
+ public static void update(final ByteBuffer buf, final int position, final
double sum, final long count)
+ {
+ writeSum(buf, position, getSum(buf, position) + sum);
+ writeCount(buf, position, getCount(buf, position) + count);
+ }
+
public static void update(ByteBuffer buf, int position, DoubleMeanHolder
other)
{
writeSum(buf, position, getSum(buf, position) + other.sum);
diff --git
a/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdDoubleMeanVectorAggregator.java
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdDoubleMeanVectorAggregator.java
new file mode 100644
index 00000000000..ec62a468d30
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/query/aggregation/simd/SimdDoubleMeanVectorAggregator.java
@@ -0,0 +1,114 @@
+/*
+ * 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.mean.DoubleMeanHolder;
+import org.apache.druid.query.aggregation.mean.DoubleMeanVectorAggregator;
+import org.apache.druid.segment.vector.VectorValueSelector;
+
+import java.nio.ByteBuffer;
+
+/**
+ * SIMD specialization of {@link DoubleMeanVectorAggregator}'s ungrouped
contiguous-range aggregation. The hot loop
+ * reduces input values into one local sum and count, then updates the mean
holder once. The grouped scatter-gather
+ * variant is inherited from the parent scalar class.
+ *
+ * Null handling is done internally: {@code DoubleMeanAggregatorFactory} is
not a {@code NullableNumericAggregatorFactory},
+ * so this aggregator is never wrapped by {@code
NullableNumericVectorAggregator} and does not implement
+ * {@code NullAwareVectorAggregator}. The contiguous-range {@link
#aggregate(ByteBuffer, int, int, int)} entry point
+ * inspects the selector's null vector itself and dispatches to a masked or
unmasked SIMD loop.
+ */
+public final class SimdDoubleMeanVectorAggregator extends
DoubleMeanVectorAggregator
+{
+ private static final VectorSpecies<Double> SPECIES =
DoubleVector.SPECIES_PREFERRED;
+
+ private final VectorValueSelector selector;
+
+ public SimdDoubleMeanVectorAggregator(final VectorValueSelector selector)
+ {
+ super(selector);
+ this.selector = selector;
+ }
+
+ @Override
+ public void aggregate(final ByteBuffer buf, final int position, final int
startRow, final int endRow)
+ {
+ final boolean[] nullVector = selector.getNullVector();
+ if (nullVector == null) {
+ aggregateNoNulls(buf, position, startRow, endRow);
+ } else {
+ aggregateNulls(buf, position, startRow, endRow, nullVector);
+ }
+ }
+
+ private void aggregateNulls(
+ final ByteBuffer buf,
+ final int position,
+ final int startRow,
+ final int endRow,
+ final 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);
+ long count = 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);
+ count += notNull.trueCount();
+ }
+ double sum = vacc.reduceLanes(VectorOperators.ADD);
+ for (; i < endRow; i++) {
+ if (!nullVector[i]) {
+ sum += vector[i];
+ count++;
+ }
+ }
+ if (count > 0) {
+ DoubleMeanHolder.update(buf, position, sum, count);
+ }
+ }
+
+ private void aggregateNoNulls(final ByteBuffer buf, final int position,
final int startRow, final 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];
+ }
+ DoubleMeanHolder.update(buf, position, sum, endRow - startRow);
+ }
+}
diff --git
a/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java
b/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java
index 4c46129f78b..56330aef6e5 100644
---
a/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java
+++
b/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java
@@ -23,12 +23,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.apache.druid.data.input.Row;
import org.apache.druid.java.util.common.granularity.Granularities;
import org.apache.druid.java.util.common.guava.Sequence;
+import org.apache.druid.math.expr.ExpressionProcessing;
import org.apache.druid.query.Druids;
import org.apache.druid.query.Query;
import org.apache.druid.query.QueryContexts;
@@ -49,6 +49,7 @@ import org.apache.druid.segment.Segment;
import org.apache.druid.segment.TestIndex;
import org.apache.druid.timeline.SegmentId;
import org.easymock.EasyMock;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
@@ -61,9 +62,13 @@ import java.util.List;
@RunWith(JUnitParamsRunner.class)
public class DoubleMeanAggregationTest
{
- public Object[] doVectorize()
+ public Object[] doVectorizeAndUseVectorApi()
{
- return Lists.newArrayList(true, false).toArray();
+ return new Object[]{
+ new Object[]{false, false},
+ new Object[]{true, false},
+ new Object[]{true, true}
+ };
}
@Rule
@@ -100,10 +105,19 @@ public class DoubleMeanAggregationTest
);
}
+ @After
+ public void resetExpressionProcessing()
+ {
+ ExpressionProcessing.initializeForTests();
+ }
+
@Test
- @Parameters(method = "doVectorize")
- public void testBufferAggretatorUsingGroupByQuery(boolean doVectorize)
throws Exception
+ @Parameters(method = "doVectorizeAndUseVectorApi")
+ public void testBufferAggretatorUsingGroupByQuery(final boolean doVectorize,
final boolean useVectorApi)
+ throws Exception
{
+ initializeExpressionProcessing(useVectorApi);
+
GroupByQuery query = new GroupByQuery.Builder()
.setDataSource("test")
.setGranularity(Granularities.ALL)
@@ -129,9 +143,15 @@ public class DoubleMeanAggregationTest
}
@Test
- @Parameters(method = "doVectorize")
- public void testVectorAggretatorUsingGroupByQueryOnDoubleColumn(boolean
doVectorize) throws Exception
+ @Parameters(method = "doVectorizeAndUseVectorApi")
+ public void testVectorAggretatorUsingGroupByQueryOnDoubleColumn(
+ final boolean doVectorize,
+ final boolean useVectorApi
+ )
+ throws Exception
{
+ initializeExpressionProcessing(useVectorApi);
+
GroupByQuery query = new GroupByQuery.Builder()
.setDataSource("test")
.setGranularity(Granularities.ALL)
@@ -153,9 +173,14 @@ public class DoubleMeanAggregationTest
}
@Test
- @Parameters(method = "doVectorize")
- public void
testVectorAggretatorUsingGroupByQueryOnDoubleColumnOnBiggerSegments(boolean
doVectorize) throws Exception
+ @Parameters(method = "doVectorizeAndUseVectorApi")
+ public void
testVectorAggretatorUsingGroupByQueryOnDoubleColumnOnBiggerSegments(
+ final boolean doVectorize,
+ final boolean useVectorApi
+ ) throws Exception
{
+ initializeExpressionProcessing(useVectorApi);
+
GroupByQuery query = new GroupByQuery.Builder()
.setDataSource("blah")
.setGranularity(Granularities.ALL)
@@ -176,9 +201,12 @@ public class DoubleMeanAggregationTest
}
@Test
- @Parameters(method = "doVectorize")
- public void testAggretatorUsingTimeseriesQuery(boolean doVectorize) throws
Exception
+ @Parameters(method = "doVectorizeAndUseVectorApi")
+ public void testAggretatorUsingTimeseriesQuery(final boolean doVectorize,
final boolean useVectorApi)
+ throws Exception
{
+ initializeExpressionProcessing(useVectorApi);
+
TimeseriesQuery query = Druids.newTimeseriesQueryBuilder()
.dataSource("test")
.granularity(Granularities.ALL)
@@ -238,4 +266,13 @@ public class DoubleMeanAggregationTest
DoubleMeanHolder meanHolder = (DoubleMeanHolder) aggregator.get();
Assert.assertEquals(2.0, meanHolder.mean(), 0.0);
}
+
+ private static void initializeExpressionProcessing(final boolean
useVectorApi)
+ {
+ if (useVectorApi) {
+ ExpressionProcessing.initializeForVectorApiTests();
+ } else {
+ ExpressionProcessing.initializeForTests();
+ }
+ }
}
diff --git
a/processing/src/test/java/org/apache/druid/query/aggregation/simd/SimdDoubleMeanVectorAggregatorTest.java
b/processing/src/test/java/org/apache/druid/query/aggregation/simd/SimdDoubleMeanVectorAggregatorTest.java
new file mode 100644
index 00000000000..2a9bcc12e42
--- /dev/null
+++
b/processing/src/test/java/org/apache/druid/query/aggregation/simd/SimdDoubleMeanVectorAggregatorTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.mean.DoubleMeanHolder;
+import org.apache.druid.query.aggregation.mean.DoubleMeanVectorAggregator;
+import
org.apache.druid.query.aggregation.simd.SimdAggregatorTestHelpers.FakeVectorValueSelector;
+import
org.apache.druid.query.aggregation.simd.SimdAggregatorTestHelpers.NullPattern;
+import org.apache.druid.testing.InitializedNullHandlingTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static
org.apache.druid.query.aggregation.simd.SimdAggregatorTestHelpers.VECTOR_SIZES;
+import static
org.apache.druid.query.aggregation.simd.SimdAggregatorTestHelpers.padNulls;
+import static
org.apache.druid.query.aggregation.simd.SimdAggregatorTestHelpers.randomDoubles;
+
+/**
+ * Equivalence test for the SIMD doubleMean vector aggregator. For each
(vector size, null pattern) tuple it drives
+ * both the SIMD aggregator and the scalar parent over the same input and
asserts the resulting mean holders match.
+ * The SIMD aggregator handles nulls internally (it is not a {@code
NullAwareVectorAggregator}), so the contiguous
+ * {@code aggregate} entry point covers both the masked and unmasked loops.
+ *
+ * Each scenario is exercised twice: once with {@code (position=0,
startRow=0)} and once with
+ * {@code (position=1, startRow=1)} where the rows before {@code startRow}
hold a large "poison" value that would
+ * visibly skew the mean 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 SimdDoubleMeanVectorAggregatorTest extends
InitializedNullHandlingTest
+{
+ private static final double POISON_DOUBLE = 1e15;
+ private static final double DELTA = 1e-9;
+
+ @Test
+ public void testDoubleMean()
+ {
+ for (int size : VECTOR_SIZES) {
+ for (NullPattern pattern : NullPattern.values()) {
+ runDoubleMean(size, pattern, 0, 0);
+ runDoubleMean(size, pattern, 1, 1);
+ }
+ }
+ }
+
+ private static void runDoubleMean(
+ final int size,
+ final NullPattern pattern,
+ final int position,
+ final 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, 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, null, values, null, nulls);
+ final int endRow = startRow + size;
+ final String msg = StringUtils.format(
+ "size[%s] nulls[%s] pos[%s] start[%s]",
+ size,
+ pattern,
+ position,
+ startRow
+ );
+
+ final DoubleMeanVectorAggregator scalar = new
DoubleMeanVectorAggregator(selector);
+ final SimdDoubleMeanVectorAggregator simd = new
SimdDoubleMeanVectorAggregator(selector);
+ final ByteBuffer scalarBuf = ByteBuffer.allocate(position +
DoubleMeanHolder.MAX_INTERMEDIATE_SIZE);
+ final ByteBuffer simdBuf = ByteBuffer.allocate(position +
DoubleMeanHolder.MAX_INTERMEDIATE_SIZE);
+ scalar.init(scalarBuf, position);
+ simd.init(simdBuf, position);
+
+ scalar.aggregate(scalarBuf, position, startRow, endRow);
+ simd.aggregate(simdBuf, position, startRow, endRow);
+ assertMeanHolderEquals(msg, scalarBuf, simdBuf, position);
+ }
+
+ private static void assertMeanHolderEquals(
+ final String msg,
+ final ByteBuffer expectedBuf,
+ final ByteBuffer actualBuf,
+ final int position
+ )
+ {
+ final DoubleMeanHolder expected = DoubleMeanHolder.get(expectedBuf,
position);
+ final DoubleMeanHolder actual = DoubleMeanHolder.get(actualBuf, position);
+ Assert.assertEquals(msg + " (count)", count(expected), count(actual));
+ Assert.assertEquals(
+ msg + " (sum)",
+ sum(expected),
+ sum(actual),
+ Math.max(Math.abs(sum(expected)) * 1e-12, DELTA)
+ );
+ Assert.assertEquals(
+ msg + " (mean)",
+ expected.mean(),
+ actual.mean(),
+ Math.max(Math.abs(expected.mean()) * 1e-12, DELTA)
+ );
+ }
+
+ private static double sum(final DoubleMeanHolder holder)
+ {
+ return ByteBuffer.wrap(holder.toBytes()).getDouble(0);
+ }
+
+ private static long count(final DoubleMeanHolder holder)
+ {
+ return ByteBuffer.wrap(holder.toBytes()).getLong(Double.BYTES);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]