Columnar execution · Rust core

Fast CPUs wait on RAM.
Keyten moves fewer bytes.

Keyten is a compact Rust dataframe engine that keeps columnar data compressed through scans, joins, grouping, windows, and time-series work.

64 bytesper Cell header

2,048 rowsper block

13block encodings

Out of coreautomatic spilling

Architecture

One 64-byte cell,
from disk to kernel.

Columns are split into 2,048-row blocks. Every block begins with a 64-byte, cache-line-aligned Cell that also serves as its on-disk record and in-memory header. Native reads mmap those records. Untouched blocks pass through a query by reference, and sealed result blocks can travel over the wire in the same format.

Kernels can work on the encoding itself. A frame-of-reference block sums as base × count + Σ residuals. Short strings pack into a u64 whose integer order matches lexical order, so comparisons, groups, and joins use the integer paths. Strings materialize when a consumer asks for them.

See how a Cell is laid out →

struct Cell64 bytes · exactly one cache line

  • identity + length: kind, enc, attrs, rc, len
  • payload pointer + size, absent for Constant and Sequence
  • meta[32]: zone map, encoding parameters, small values inline
diskcellmmap, zero copy
memorycellshared by pointer
wirecellsealed bytes travel
kernelcellcomputes encoded

Encodings

Every block picks
its own encoding.

Every 2,048-row block chooses an encoding from its values. Neighbors may disagree. One run of IDs can become Sequence, while the next block collapses thousands of repeated values into a payload-free Constant header. Try each data shape and watch the builder choose.

the block2,048 rows

7 7 7 7 7 7 7 7 7 7 7 7 …

builder: one distinct → Constant { 7 }

the choiceCONST

One value rides in the header. The block allocates zero payload bytes.

plain
16 KiB
encoded
0 B

The writer makes column-wide choices too. It records a distinct-count sketch, then adds a global dictionary, an FSST symbol table, or a per-value block index when the measured cost says the structure will pay for itself. Their calibration constants live in one cost model, with the source for each number beside it.

Statistics

Prove which blocks
need decoding.

Keyten checks statistics first. A zone map can settle an entire comparison while its payload remains sealed. For equality checks, per-value block indexes point straight at the few blocks that might match, while projection pushdown leaves every unused column file closed.

Parquet scans follow the same path. Keyten reads pages natively, seals them into its encodings, and builds zone maps as it goes, which lets footer statistics eliminate entire row groups. For some files, count, min, and max come from the footer alone.

How pushdown decides →

df.filter(kt.col("px") > 250)zone maps, one column

b00212–239unread
b01218–247unread
b02236–255scan
b03251–260proven true
b04199–230unread
b05244–258scan
b06205–241unread
b07209–246unread

Only two payloads need decoding. Five blocks fall entirely below 250. Block b03 sits above it and produces a constant-true mask from metadata.

Execution

One block size,
across the whole query.

The executor works in storage-sized blocks. Workers pull them through fused filter and map stages, then feed aggregation, sort, window, or join sinks. At those same boundaries, Keyten reports progress, checks for cancellation, and adjusts parallelism to fit the machine running the query.

Stateful operators get a budget from the host. When a sort, group-by, or join outgrows that budget, it writes ordinary native tables to disk and continues from there.

Read the execution model →

one query, every corework-stealing workers

source
w0filter ∘ map
w1filter ∘ map
w2filter ∘ map
sinksgroupsortjoinwindow

blocks 1,481 / 4,096 · rows 3.0Mcancel lands at the next block boundary

Data can outgrow RAM. Sorts write and merge runs. Group-bys and joins partition to disk, while map pipelines stream with constant memory.

Scale

Run the same plan
on a worker fleet.

A single machine eventually runs into its memory bandwidth. collect_distributed() serializes the lazy plan, sends native-scan fragments to a supplied worker list, and streams compressed result blocks back to the caller. Decomposable aggregates merge across the fleet. A serializable plan that cannot split runs whole on one worker; an in-memory plan stays local. distributed execution: landing · under active development

the querylazy plan

query = (
    kt.scan_native("trades.k10dir")
      .filter(kt.col("qty") > 100)
      .group_by("sym")
      .agg(kt.col("notional").sum())
)

plan ⇢ versioned wire form · results ⇠ compressed blocks

laptopevery core

query.collect()

One process. Keyten selects parallelism and a spill budget from the host.

fleetown channels each

query.collect_distributed(workers)
w0 w1 w2

Workers compute mergeable partial aggregates in the same engine. Unsplittable file plans run whole on one worker.

Proof

The claims come with
tests and scripts.

The test suite runs every rewritten plan beside the original and compares their results, which catches an optimizer rule that changes the answer. explain() prints the plan before it runs. Published benchmark results include the commands needed to reproduce them from a fresh clone.

collect(optimize(plan))collect(plan) bit-exact · enforced across the test suite
$pip install keyten
See the benchmarks
Keyten

A compact Rust dataframe engine that keeps columnar data compressed through whole queries.

© 2026 Rayforce Technologies Inc. All rights reserved.