Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
adacd58
Add design docs for Rust acceleration initiative
May 21, 2026
3ca22a1
Add pinot-native module — Rust SUM kernel + JNI bindings
May 21, 2026
3126675
Route eligible aggregations through native engine (WIP)
May 21, 2026
d22cae2
Make NativeSumAggregationFunctionTest checkstyle-clean
May 21, 2026
c7f9d32
Replace autovec SUM kernel with explicit SIMD intrinsics
May 22, 2026
69d62f3
Add ASF license headers to design docs, Cargo manifests, README
May 28, 2026
db08669
Add JMH microbenchmark for native vs Java SUM(LONG)
siddharthteotia May 28, 2026
1f393ea
Add scalar Rust SUM kernel JMH variant for three-way attribution
siddharthteotia May 30, 2026
5e34ea0
Lock Phase 1 agg scope, document SwissTable sharing
siddharthteotia May 30, 2026
d368e80
Add Rust SUM kernels and JNI entry points for INT/FLOAT/DOUBLE
siddharthteotia May 30, 2026
7f09b55
Route SUM(INT/FLOAT/DOUBLE) through native engine
siddharthteotia May 30, 2026
7403bbf
Port pinot-native module to li-pinot base (version + BlockValSet drift)
siddharthteotia May 30, 2026
ecad632
Add per-type scalar JNI entries for three-way SUM attribution
siddharthteotia May 30, 2026
035cc9e
Extend SUM JMH to per-type matrix, write back §11.B
siddharthteotia May 30, 2026
3294e67
Add Rust MIN/MAX kernels and JNI entries for INT/LONG/FLOAT/DOUBLE
siddharthteotia May 31, 2026
f40da0e
Route MIN/MAX/COUNT through native engine
siddharthteotia May 31, 2026
1f60437
Document Phase 1.B.2 (MIN/MAX/COUNT) in design doc
siddharthteotia May 31, 2026
0e6efdb
Add Phase 1.D-core SwissTable + Path C dual backends + segment driver
siddharthteotia Jun 4, 2026
6f76c87
Add MIN/MAX three-way JMH attribution harness (Phase 1.B.2 follow-up)
siddharthteotia Jun 4, 2026
87dc4f7
Plan step (1a) WIP: multi-agg driver core + autovec verification + ro…
siddharthteotia Jun 8, 2026
8d66eb5
Design doc §21: per-type speedup pedagogical reference for PR comments
siddharthteotia Jun 8, 2026
d29ef36
Fix checkstyle: NativeAggKind field ordinalByte -> _ordinalByte
Jun 19, 2026
8e0eed4
Plan step (1a): add f64-accumulator SUM kinds for Pinot group-by parity
Jun 19, 2026
c4d18c8
Design: lock native server combine + B1 seam for GROUP BY
Jun 19, 2026
6b87e88
Plan step (1b): wire native segment GROUP BY into the SSE operator
Jun 19, 2026
b0f393a
Design §19.0: mark step 1b done, set native combine as next
Jun 19, 2026
40b98fb
Design §22: full session log for 2026-06-19
Jun 19, 2026
ccbaeee
Add native GROUP BY JMH harness + backend toggle
Jun 19, 2026
b996536
Design §22.8.1: capture combine threading + raw-value-key Q&A verbatim
Jun 19, 2026
bab149e
Add gated attribution profiling to native GROUP BY (§22.9 lever 0)
Jun 20, 2026
75b224d
Task #54: native server-combine core — radix-partitioned parallel merge
Jun 20, 2026
e60082f
Foundation #1: dict-direct backend for dict-encoded segment keys
Jun 20, 2026
f372ebb
Foundation #2: multi-key support — generic driver + packed-key encoder
Jun 20, 2026
ebe0726
Foundation #4a: CanonicalF32/F64 keys (Task #45) — FP GROUP BY keys
Jun 20, 2026
0a9ca78
Design §23: SOTA GROUP BY foundation — key-encoding architecture + bu…
Jun 20, 2026
d7da10e
Foundation #4b: STRING arena — variable-length GROUP BY keys (Task #53)
Jun 20, 2026
d872abb
Design §23: mark STRING arena (#4b) done, key-type foundation complete
Jun 20, 2026
2b4f0c6
Step 5 wiring (first slice): native server combine callable from Java…
Jun 20, 2026
6281b24
Design §23: log the combine JNI wiring first slice (LONG keys)
Jun 20, 2026
50b635a
Native combine: unify FFI/bridge over all key types (LONG/DOUBLE/STRING)
Jun 22, 2026
5f92e39
Design §24-§26: combine result-path, verbatim session log, limiting m…
Jun 22, 2026
8f6758f
Design §27: boundary model + Pattern A/B + boundary-2-first decision …
Jun 22, 2026
4eef1ca
Native combine: ORDER BY top-K / result selection core (boundary-2, s…
Jun 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
401 changes: 401 additions & 0 deletions RUST_REWRITE_DESIGN.md

Large diffs are not rendered by default.

5,133 changes: 5,133 additions & 0 deletions docs/native/phase-1-design.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions pinot-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@
<groupId>org.apache.pinot</groupId>
<artifactId>pinot-timeseries-spi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.pinot</groupId>
<artifactId>pinot-native</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.apache.pinot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
import org.apache.pinot.core.query.aggregation.function.AggregationFunctionUtils.AggregationInfo;
import org.apache.pinot.core.query.aggregation.groupby.DefaultGroupByExecutor;
import org.apache.pinot.core.query.aggregation.groupby.GroupByExecutor;
import org.apache.pinot.core.query.aggregation.groupby.NativeGroupByExecutor;
import org.apache.pinot.core.query.aggregation.groupby.NativeGroupByRouter;
import org.apache.pinot.core.query.request.context.QueryContext;
import org.apache.pinot.core.startree.executor.StarTreeGroupByExecutor;
import org.apache.pinot.core.util.GroupByUtils;
Expand Down Expand Up @@ -108,6 +110,8 @@ protected GroupByResultsBlock getNextBlock() {
GroupByExecutor groupByExecutor;
if (_useStarTree) {
groupByExecutor = new StarTreeGroupByExecutor(_queryContext, _groupByExpressions, _projectOperator);
} else if (NativeGroupByRouter.shouldAccelerate(_queryContext, _groupByExpressions, _projectOperator)) {
groupByExecutor = new NativeGroupByExecutor(_queryContext, _groupByExpressions, _projectOperator);
} else {
groupByExecutor = new DefaultGroupByExecutor(_queryContext, _groupByExpressions, _projectOperator);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ public static AggregationFunction getAggregationFunction(FunctionContext functio
List<ExpressionContext> arguments = function.getArguments();
int numArguments = arguments.size();
ExpressionContext firstArgument = arguments.get(0);
// Single integration point for the native (Rust+JNI) acceleration path. See
// NativeAggregationRouter for eligibility rules. When disabled or ineligible,
// construction falls through to the standard Java factory branches below.
if (NativeAggregationRouter.shouldAccelerate(upperCaseFunctionName, arguments, nullHandlingEnabled)) {
return NativeAggregationRouter.createNative(upperCaseFunctionName, arguments, nullHandlingEnabled);
}
if (upperCaseFunctionName.startsWith("PERCENTILE")) {
String remainingFunctionName = upperCaseFunctionName.substring(10);
if (remainingFunctionName.equals("SMARTTDIGEST")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* 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.pinot.core.query.aggregation.function;

import java.util.List;
import org.apache.pinot.common.request.context.ExpressionContext;
import org.apache.pinot.nativeengine.agg.PinotNativeAgg;


/**
* Routes eligible aggregation function constructions to the native (Rust+JNI) acceleration
* path. This is the single integration point between Pinot's aggregation engine and
* {@code pinot-native}.
*
* <p>Eligibility checks (short-circuiting):
* <ol>
* <li>Feature flag {@code pinot.native.aggregation.enabled} is set to {@code true}</li>
* <li>The native library loaded successfully ({@link PinotNativeAgg#isAvailable()})</li>
* <li>Null handling is disabled (no native null path yet)</li>
* <li>Function type is in the Phase 1.B scope: {@code SUM} / {@code SUM0} / {@code MIN} /
* {@code MAX} / {@code COUNT}</li>
* <li>The aggregated expression is a simple column identifier (no transforms) — for COUNT
* this includes {@code *}</li>
* </ol>
*
* <p>When any check fails the caller must construct the original Java AggregationFunction
* unchanged. The router never throws.
*
* <p>Routing here covers all aggregation contexts in Pinot — SSE V1, MSE leaf (which
* delegates to V1), MSE intermediate, star-tree, realtime consuming segments, and
* Materialized View refresh — because every one of them obtains AggregationFunction
* instances from {@link AggregationFunctionFactory}. See {@code docs/native/phase-1-design.md}
* §2 for the engine landscape.
*/
public final class NativeAggregationRouter {

/** System property gating the native engine. Default {@code false}. */
public static final String ENABLED_PROPERTY = "pinot.native.aggregation.enabled";

private NativeAggregationRouter() {
}

/**
* @return {@code true} if a native AggregationFunction should be constructed for the
* given function name + arguments. Caller must use {@link #createNative} to do so.
*/
public static boolean shouldAccelerate(
String upperCaseFunctionName, List<ExpressionContext> arguments,
boolean nullHandlingEnabled) {
if (!enabled()) {
return false;
}
if (!PinotNativeAgg.isAvailable()) {
return false;
}
if (nullHandlingEnabled) {
return false;
}
if (!isInScopeFunction(upperCaseFunctionName)) {
return false;
}
return isSimpleColumnArg(arguments);
}

/**
* Constructs a native AggregationFunction for a name + arguments combination previously
* accepted by {@link #shouldAccelerate}. Caller is responsible for the eligibility check.
*
* @throws IllegalStateException if the function name is not in scope (programming error)
*/
@SuppressWarnings("rawtypes")
public static AggregationFunction createNative(
String upperCaseFunctionName, List<ExpressionContext> arguments,
boolean nullHandlingEnabled) {
switch (upperCaseFunctionName) {
case "SUM":
case "SUM0":
return new NativeSumAggregationFunction(arguments, nullHandlingEnabled);
case "MIN":
return new NativeMinAggregationFunction(arguments, nullHandlingEnabled);
case "MAX":
return new NativeMaxAggregationFunction(arguments, nullHandlingEnabled);
case "COUNT":
return new NativeCountAggregationFunction(arguments, nullHandlingEnabled);
default:
throw new IllegalStateException(
"Native AggregationFunction requested for unsupported function: "
+ upperCaseFunctionName);
}
}

static boolean enabled() {
return Boolean.getBoolean(ENABLED_PROPERTY);
}

private static boolean isInScopeFunction(String upperCaseFunctionName) {
switch (upperCaseFunctionName) {
case "SUM":
case "SUM0":
case "MIN":
case "MAX":
case "COUNT":
return true;
default:
return false;
}
}

private static boolean isSimpleColumnArg(List<ExpressionContext> arguments) {
if (arguments.size() != 1) {
return false;
}
return arguments.get(0).getType() == ExpressionContext.Type.IDENTIFIER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* 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.pinot.core.query.aggregation.function;

import java.util.List;
import java.util.Map;
import org.apache.pinot.common.request.context.ExpressionContext;
import org.apache.pinot.core.common.BlockValSet;
import org.apache.pinot.core.query.aggregation.AggregationResultHolder;


/**
* COUNT aggregation accelerated by the native engine — but notably without a kernel.
*
* <p>Under {@link NativeAggregationRouter}'s gating (null handling disabled, simple
* column identifier or absent expression), both {@code COUNT(*)} and {@code COUNT(col)}
* collapse to "block length added to the result holder". Crossing JNI for this would
* add the ~85 ns FFI fixed cost from §11.A of the design doc with zero kernel benefit.
*
* <p>So the "native" path here is just: {@code holder.setValue(holder.getDoubleResult() + length)},
* inline in Java. The class still exists so the routing infra in
* {@link NativeAggregationRouter} can be uniform across SUM/MIN/MAX/COUNT — every routed
* function gets its own {@code Native*AggregationFunction} class, even when the
* implementation is trivial.
*
* <p>Star-tree pre-aggregated case (where {@code blockValSetMap} is not empty and
* {@code nullHandlingEnabled=false} but the expression is the star-tree-internal
* {@code COUNT_STAR_EXPRESSION}, requiring a {@code long[]} sum) and null-handling case
* both fall through to the Java parent. {@link NativeAggregationRouter#shouldAccelerate}
* already disqualifies them, but we double-guard here for safety.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class NativeCountAggregationFunction extends CountAggregationFunction {

public NativeCountAggregationFunction(List<ExpressionContext> arguments,
boolean nullHandlingEnabled) {
super(arguments, nullHandlingEnabled);
}

@Override
public void aggregate(int length, AggregationResultHolder aggregationResultHolder,
Map<ExpressionContext, BlockValSet> blockValSetMap) {
// Fast path: COUNT(*) — blockValSetMap is empty per CountAggregationFunction's contract,
// so the value to add IS the block length.
if (blockValSetMap.isEmpty()) {
aggregationResultHolder.setValue(aggregationResultHolder.getDoubleResult() + length);
return;
}
// Anything else (null-handling enabled, star-tree pre-aggregated) — defer to Java.
// NativeAggregationRouter.shouldAccelerate should disqualify these at construction time,
// but we double-guard.
super.aggregate(length, aggregationResultHolder, blockValSetMap);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* 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.pinot.core.query.aggregation.function;

import java.util.List;
import java.util.Map;
import org.apache.pinot.common.request.context.ExpressionContext;
import org.apache.pinot.core.common.BlockValSet;
import org.apache.pinot.core.query.aggregation.AggregationResultHolder;
import org.apache.pinot.nativeengine.agg.PinotNativeAgg;


/**
* MAX aggregation accelerated by the native (Rust+JNI) engine. Mirror of
* {@link NativeMinAggregationFunction} for {@code MAX} semantics: dispatches
* single-value INT/LONG/FLOAT/DOUBLE columns to the matching {@link PinotNativeAgg}
* kernel; other type / encoding combinations fall through to the Java parent.
*
* <p>NaN semantics match the parent's {@code Math.max(...)} — NaN propagates.
* Empty input returns {@link Double#NEGATIVE_INFINITY}.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class NativeMaxAggregationFunction extends MaxAggregationFunction {

public NativeMaxAggregationFunction(List<ExpressionContext> arguments,
boolean nullHandlingEnabled) {
super(arguments, nullHandlingEnabled);
}

@Override
public void aggregate(int length, AggregationResultHolder aggregationResultHolder,
Map<ExpressionContext, BlockValSet> blockValSetMap) {
BlockValSet blockValSet = blockValSetMap.get(_expression);
if (blockValSet.isSingleValue()) {
double blockMax = Double.NaN;
switch (blockValSet.getValueType().getStoredType()) {
case INT:
blockMax = PinotNativeAgg.maxInt(blockValSet.getIntValuesSV(), length);
break;
case LONG:
blockMax = PinotNativeAgg.maxLong(blockValSet.getLongValuesSV(), length);
break;
case FLOAT:
blockMax = PinotNativeAgg.maxFloat(blockValSet.getFloatValuesSV(), length);
break;
case DOUBLE:
blockMax = PinotNativeAgg.maxDouble(blockValSet.getDoubleValuesSV(), length);
break;
default:
break;
}
// See NativeMinAggregationFunction for the NaN-vs-error rationale.
if (!Double.isNaN(blockMax)) {
double prev = aggregationResultHolder.getDoubleResult();
aggregationResultHolder.setValue(Math.max(blockMax, prev));
return;
}
}
super.aggregate(length, aggregationResultHolder, blockValSetMap);
}
}
Loading
Loading