Open-Source Wikis

/

Apache Arrow

/

Systems

/

Gandiva

apache/arrow

Gandiva

Active contributors: Sutou Kouhei, Dmitry Chirkov, Logan Riggs, Antoine Pitrou

Gandiva is an LLVM-based JIT compiler for Arrow expressions. It lives in cpp/src/gandiva/ and was contributed by Dremio in May 2018. Given a tree of Arrow Expressions, Gandiva produces native machine code that filters or projects record batches at runtime — typically much faster than interpreting expressions kernel-by-kernel.

Purpose

Compile vectorized expressions to native code. The classical way to evaluate (a + b) * c > 10 is to call three compute kernels in sequence, materializing intermediate columns. Gandiva fuses that whole expression tree into a single function that does one pass over the record batch.

Two main entry points

Class File Use
gandiva::Projector projector.h Compiles a list of expressions; given a record batch, produces output arrays.
gandiva::Filter filter.h Compiles a single boolean expression; given a record batch, produces a selection vector.

A typical session:

auto schema = arrow::schema({field("a", int32()), field("b", int32())});

auto a = TreeExprBuilder::MakeField(schema->field(0));
auto b = TreeExprBuilder::MakeField(schema->field(1));
auto sum = TreeExprBuilder::MakeFunction("add", {a, b}, int32());
auto expr = TreeExprBuilder::MakeExpression(sum, field("sum", int32()));

std::shared_ptr<gandiva::Projector> projector;
ARROW_RETURN_NOT_OK(gandiva::Projector::Make(schema, {expr}, &projector));

arrow::ArrayVector outputs;
ARROW_RETURN_NOT_OK(projector->Evaluate(*batch, pool, &outputs));

Pipeline

graph LR
    Tree["TreeExprBuilder builds Node tree"]
    Tree --> Decompose["ExprDecomposer: simplify + lift validity"]
    Decompose --> DEX["DEX: typed intermediate representation"]
    DEX --> LLVM["LLVM IR generation (llvm_generator.cc)"]
    LLVM --> Bitcode["Link with precompiled bitcode (math, regex, decimal)"]
    Bitcode --> Optimizer["LLVM optimizer passes"]
    Optimizer --> JIT["JIT compile to native code"]
    JIT --> Cache["Cache by ExpressionKey"]
    Cache --> Run["Run over record batches"]

Files

File Purpose
node.h, tree_expr_builder.{h,cc} The expression tree and its builder.
expr_decomposer.{h,cc} Lowers an expression to a DEX (decomposed expression) IR that exposes validity propagation explicitly.
dex.h, dex_visitor.h The DEX nodes (literal, field reference, function, if-then-else, ...).
llvm_generator.{h,cc} (~58 KB), llvm_includes.h, llvm_types.{h,cc} LLVM IR emission. The core of Gandiva.
engine.{h,cc} LLVM ORC JIT setup. Loads the precompiled bitcode and optimizes/compiles modules.
precompiled/ C++ helpers compiled to LLVM bitcode at build time. Includes regex, decimal arithmetic, hashing, casting, datetime handling.
precompiled_bitcode.cc.in Template that wraps the bitcode bytes into a C++ symbol the JIT can load.
make_precompiled_bitcode.py, GandivaAddBitcode.cmake Build-time codegen scripts.
function_registry*.{h,cc} The registry of supported functions, split by category (arithmetic, string, datetime, hash, math_ops, timestamp_arithmetic).
function_holder.h + *_holder.{h,cc} Per-function state (e.g., compiled regex objects).
gdv_function_stubs*.{cc,h} (~113 KB total) C++ stubs that DEX calls jump to (regex match, decimal divide, etc.). These are also exposed to the JIT.
selection_vector.{h,cc} Output of Filter — a list of row indices.
cache.{h,cc}, lru_cache.h LRU cache keyed on ExpressionCacheKey so repeated expressions don't re-JIT.
projector.{h,cc}, filter.{h,cc} The two public entry points.
expression.h, condition.h The expression and filter wrappers users build through TreeExprBuilder.
expression_registry.{h,cc} Lists the supported types/functions for client introspection.
interval_holder.{h,cc}, to_date_holder.{h,cc}, random_generator_holder.{h,cc} Per-call state for stateful functions.

Precompiled bitcode

Gandiva ships a non-trivial chunk of code as precompiled LLVM bitcode rather than emitting it from scratch each time. This includes:

  • Decimal128 / Decimal256 arithmetic (cpp/src/gandiva/decimal_ir.cc, decimal_xlarge.cc).
  • Regex matching (uses re2).
  • Hash functions for ints, doubles, strings.
  • Date/time conversions.
  • Casts that aren't trivially expressible in LLVM IR (e.g., string ↔ decimal).

The build pipeline:

  1. C++ source in cpp/src/gandiva/precompiled/ is compiled by clang to LLVM bitcode (irhelpers.bc).
  2. make_precompiled_bitcode.py wraps the bitcode bytes into a C++ source file.
  3. The resulting object is linked into libgandiva.so.
  4. At runtime, the JIT loads the bitcode module and merges it with the user-generated module before optimization.

This is why Gandiva builds need clang available even when the rest of Arrow is built with GCC.

Function registry

The Gandiva function registry is separate from the Arrow compute function registry. They cover overlapping but not identical sets of functions, with Gandiva's set focused on what's worth JIT-compiling. Categories:

  • function_registry_arithmetic.ccadd, subtract, multiply, divide, mod, power.
  • function_registry_string.cc (~30 KB) — substring, concat, lower, upper, regexp_matches, regexp_replace, split_part, ...
  • function_registry_datetime.ccdate_add, date_diff, extract_year, to_date.
  • function_registry_hash.cchash, hash32, hash64.
  • function_registry_math_ops.ccfloor, ceil, round, sqrt, log, etc.
  • function_registry_timestamp_arithmetic.cc — timestamp arithmetic.

Each registry file calls RegisterFunction with a NativeFunction describing the input/output types and the symbol to call.

Caching

Compiled projectors and filters are cached by ExpressionCacheKey (a hash over the expression tree + schema). The cache is bounded by lru_cache.h. Re-evaluating the same expression on a new batch is essentially free of compilation cost.

Test infrastructure

cpp/src/gandiva/tests/ contains projector_test.cc (38 KB), filter_test.cc, and end-to-end tests covering each function category. cpp/src/gandiva/gdv_function_stubs_test.cc (58 KB) tests every C++ stub.

Language wrapper integration

  • PyArrow exposes Gandiva via python/pyarrow/gandiva.pyx. Public API in pyarrow.gandiva.
  • R does not expose Gandiva directly (it uses Acero / compute kernels instead).
  • C-GLib + Ruby ship gandiva-glib and red-gandiva.

Performance niches

Gandiva tends to outperform the compute kernel chain when:

  • An expression has many operations and the kernel chain would materialize many intermediate columns.
  • The data has high null ratios (Gandiva's lifted validity propagation skips work).
  • Type combinations are unusual enough that a single fused codepath is much tighter than the kernel registry path.

For simple single-kernel calls, the compute layer is usually faster because no JIT compilation cost is amortized.

Entry points for modification

  • Adding a new function: register it in one of the function_registry_*.cc files; provide either a precompiled-bitcode helper (in precompiled/) or a gdv_function_stubs.cc C++ stub.
  • Improving codegen: llvm_generator.cc is where most LLVM IR is emitted. engine.cc controls the optimizer pass pipeline.
  • Adding a Holder: see existing regex_functions_holder.cc, interval_holder.cc, to_date_holder.cc.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Gandiva – Apache Arrow wiki | Factory