apache/arrow
Compute kernels
Active contributors: Rossi Sun, Antoine Pitrou, Hyukjin Kwon, Felipe Aramburu
The compute layer (cpp/src/arrow/compute/) is Arrow's vectorized function library. It exposes ~150 named functions — arithmetic, comparison, casting, string operations, sorting, aggregation, and more — that operate on Datum (Array, ChunkedArray, RecordBatch, Table, or Scalar) inputs.
Three function categories
| Category | Output shape | Examples |
|---|---|---|
| Scalar | Same length as input | add, divide, equal, cast, utf8_lower, is_null, coalesce |
| Vector | Possibly different shape | take, filter, unique, sort_indices, partition_nth_indices, array_sort_indices |
| Aggregate | Single scalar (scalar aggregate) or grouped result (hash aggregate) | sum, mean, count, tdigest, quantile, hash_count_distinct, pivot_wider |
Scalar functions can run inside SQL-like context. Vector functions are not safe for SQL because the output size and ordering depend on the entire input. Aggregates have two forms:
- Scalar aggregate — produces a single value over the whole input.
- Hash aggregate — produces grouped results (
SELECT key, sum(x) FROM t GROUP BY key). Implemented incpp/src/arrow/acero/groupby_aggregate_node.ccand the supporting kernels incpp/src/arrow/compute/kernels/hash_aggregate.cc.
Architecture
graph LR
UserAPI["User API: arrow::compute::CallFunction(name, args)"] --> Registry["FunctionRegistry"]
Registry --> Function["Function (e.g. ScalarFunction 'add')"]
Function --> KernelDispatch["Kernel selection by input types"]
KernelDispatch --> Kernel["Kernel (e.g. add<Int32, Int32>)"]
Kernel --> Exec["KernelExecutor"]
Exec --> Output["ArraySpan output"]Key types in cpp/src/arrow/compute/:
| Type | Header | Purpose |
|---|---|---|
Function |
function.h |
A named function with one or more kernels. Subclasses: ScalarFunction, VectorFunction, ScalarAggregateFunction, HashAggregateFunction. |
Kernel |
kernel.h |
A single implementation. Knows how to handle a specific combination of input types. |
KernelInit |
kernel.h |
Optional per-call state initialization. |
KernelContext |
kernel.h |
Per-call state, exec context, error reporting. |
FunctionRegistry |
registry.h |
Process-wide registry of every Function. |
Expression |
expression.h |
A literal, field reference, or function call. Used by Acero and the dataset scanner for predicate pushdown. |
KernelExecutor |
exec.h, exec_internal.h |
Drives a kernel over Datum inputs; handles chunking, validity propagation, and broadcasting. |
The function registry
A global registry maps function name → Function object. The default registry is populated by arrow::compute::Initialize() (called automatically on first use), which calls registration functions in cpp/src/arrow/compute/kernels/. Examples:
RegisterScalarArithmetic(scalar_arithmetic.cc) —add,subtract,multiply,divide,power,negate,abs.RegisterScalarBoolean(scalar_boolean.cc) —and,or,xor,not.RegisterScalarString(scalar_string.cc) —utf8_upper,utf8_lower,match_substring,replace_substring.RegisterScalarCast(scalar_cast_*.cc) — everycast(*)overload.RegisterVectorSort(vector_sort.cc) —sort_indices,array_sort_indices,partition_nth_indices.RegisterScalarAggregate(aggregate_basic.cc,aggregate_quantile.cc,aggregate_tdigest.cc, etc.) —sum,mean,count,min_max,quantile,tdigest,mode.RegisterHashAggregate(hash_aggregate.cc,hash_aggregate_numeric.cc, etc.) —hash_sum,hash_count,hash_count_distinct,hash_pivot_wider.
User code calls arrow::compute::CallFunction("name", {datum1, datum2}, options) and the registry handles dispatch.
Kernel dispatch
A ScalarFunction may have many kernels — one per (input type, ...) tuple. Dispatch happens in two stages:
- Type matching.
Kernel::signaturedeclares acceptable input types.Function::DispatchExactlooks for an exact match;Function::DispatchBestallows implicit casts. - State init. Kernels that need per-call state (e.g.
castneeds target type info) implementKernelInit.
The exact match path is hot — production engines like Acero pre-resolve kernels at plan time so per-batch dispatch is just a function pointer call.
Kernels directory
cpp/src/arrow/compute/kernels/ is the largest single directory in the compute tree. It holds kernels for every category. Notable files:
| File | Kernels |
|---|---|
scalar_arithmetic.cc (~180 KB) |
All elementwise arithmetic |
scalar_string_ascii.cc / scalar_string_utf8.cc |
String operations split by encoding |
scalar_cast_*.cc |
Cast kernels split by target type family |
scalar_compare.cc |
equal, less, less_equal, etc. |
vector_sort.cc |
Sorting by indices |
vector_selection_*.cc |
take, filter, drop_null |
vector_hash.cc |
unique, value_counts, dictionary_encode |
aggregate_basic.cc (with _avx2 and _avx512 variants) |
sum, mean, min_max, count |
aggregate_quantile.cc, aggregate_tdigest.cc, aggregate_var_std.cc, aggregate_pivot.cc, aggregate_mode.cc |
Specialized scalar aggregates |
hash_aggregate.cc (with numeric, pivot siblings) |
Hash-grouped aggregates |
Expressions
arrow::compute::Expression (cpp/src/arrow/compute/expression.h) is a small, immutable expression tree:
auto expr = greater(field_ref("x"), literal(5));
auto bound = expr.Bind(*schema);
ARROW_ASSIGN_OR_RAISE(Datum result, ExecuteScalarExpression(bound, batch));It supports literal nodes, field references, and function calls. Bound expressions cache resolved kernels for fast repeated execution. The Acero filter and project nodes evaluate Expressions; the dataset scanner uses them for filter pushdown to Parquet column statistics.
CPU dispatch
Several kernels have multiple SIMD variants. The pattern:
- A header file defines an interface (
scalar_arithmetic_internal.h). - A non-vectorized
.ccprovides the scalar fallback. - Sibling
_avx2.cc,_avx512.cc,_neon.cc,_sve.ccfiles provide vectorized paths. - Build flags compile the SIMD variants only when the toolchain supports them.
- Runtime selection happens via
arrow::internal::CpuInfo(cpp/src/arrow/util/cpu_info.cc).
Recent commits like GH-49756 ("[C++] SVE dynamic dispatch") added ARM SVE selection.
Hashing
cpp/src/arrow/compute/key_hash_internal.cc (with an AVX2 variant) implements the multi-column hashing used by hash joins and hash aggregates. key_map_internal.cc implements the open-addressing hash map. Both are also reused by Acero's swiss-table-based hash join.
Row encoding
cpp/src/arrow/compute/row/ holds the row-format encoding used by hash aggregates and joins. A RowTableImpl packs the keys and values for a group into a contiguous row buffer that's friendly to hash table probes.
Testing
Each kernel has tests in kernels/*_test.cc:
scalar_arithmetic_test.cc,scalar_compare_test.cc,scalar_cast_test.cc,scalar_string_test.ccaggregate_test.cc— scalar aggregatesvector_sort_test.cc,vector_selection_test.cc,vector_hash_test.cc
Test helpers live in cpp/src/arrow/compute/test_util_internal.cc and cpp/src/arrow/testing/. The compute test suites are some of the largest in the project — aggregate_test.cc is ~4,900 lines, scalar_cast_test.cc is ~4,500.
Entry points for modification
- Adding a new function: declare in a kernels file, write the kernel(s), and register in a
RegisterXfunction. Add a test in*_test.ccand a Python binding inpython/pyarrow/_compute.pyxif desired. - Adding a SIMD path: create a
*_avx2.ccor*_avx512.ccsibling, add it to CMake with the correct compile flags (-mavx2etc.), and register the variant in the dispatch. - Optimizing a hot kernel: profile with the matching
*_benchmark.cc. The compute benchmark binaries are run by Conbench.
See Acero for how compute functions are composed into query pipelines.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.