Open-Source Wikis

/

Apache Arrow

/

Systems

/

Compute kernels

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 in cpp/src/arrow/acero/groupby_aggregate_node.cc and the supporting kernels in cpp/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) — every cast(*) 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:

  1. Type matching. Kernel::signature declares acceptable input types. Function::DispatchExact looks for an exact match; Function::DispatchBest allows implicit casts.
  2. State init. Kernels that need per-call state (e.g. cast needs target type info) implement KernelInit.

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 .cc provides the scalar fallback.
  • Sibling _avx2.cc, _avx512.cc, _neon.cc, _sve.cc files 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.cc
  • aggregate_test.cc — scalar aggregates
  • vector_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 RegisterX function. Add a test in *_test.cc and a Python binding in python/pyarrow/_compute.pyx if desired.
  • Adding a SIMD path: create a *_avx2.cc or *_avx512.cc sibling, add it to CMake with the correct compile flags (-mavx2 etc.), 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.

Compute kernels – Apache Arrow wiki | Factory