Skip to content

asap-executor: a columnar physical operator library (state columns, per-sample updaters) for runtimes executing the DAG #762

Closed

Description

@zzylol

Problem, by example

ASAPQuery-backend executes ASAPPlanner's installed DAG with asap-executor's operators. The test case is the planner-layering PromQL examples on 2,000 series (90 minutes, 15 s samples), on a release build with perf sampling. The data plane spent 53 CPU core-seconds, and the sketch kernels were only 3–7% of that. The rest comes from the executor's row layout: a Batch is Vec<Vec<Value>>, one Vec<Value> per row.

Where the time goes (share of data-plane CPU)
malloc / free 32%
row clones 11%
grouping keys built per row (Vec<Vec<u8>>) 9%
memory reservation recomputed per row 9%
row drops 5%
batch validation 2%
Row layout total 68% (75% without raw data retained)

The same work in ASAPQuery's engine, single core, same data:

asap-executor (rows) ASAPQuery
Pane construction 10.5 µs/sample 2.3 µs/sample
sum by (job) (rate(...)) over stored panes 15.4 ms 4.1 ms
60-minute quantile over stored panes 62.2 ms 33.5 ms

The data plane must execute the DAG node by node for every query shape. No shape-matched fast path beside it is allowed. So the operators themselves must get cheaper.

Proposal: asap-executor becomes a columnar physical operator library

ASAPPlanner provides the physical operator library: one implementation per plan-node kind. The runtime's execution engine walks the DAG and calls those operators. The library also keeps a minimal single-threaded DAG runner, for the Planner's own tests and for InstallablePlan::query_time_reference.

Batch format. A batch is a set of equal-length columns plus a selection vector; a filter marks rows instead of copying them.

Column kind Layout
Numeric Vec<f64> / Vec<i64> plus a validity bitmap
Series id Vec<u32> into a per-run series table of interned Arc<LabelSet>. Labels are decoded only when results leave the engine.
Group id Vec<u32> into the operator's group table. The group of a series is computed once and cached by series id.
Label / string Dictionary-encoded
Summary state A state column (below)

Batches hold up to a fixed row limit (e.g. 8,192), so memory accounting and validation are paid per batch.

State columns: how sketches are columnar. One state per group, contiguous. All states in a column share one family and one parameter set, taken from the plan.

  • Exact accumulators (sum, count, min, max, last) are plain numeric columns.
  • Fixed-size sketches (Count-Min, Count Sketch, HLL registers) can share one flat groups × size buffer. Merge is element-wise.
  • Variable-size sketches (KLL, DDSketch, top-k heaps, UnivMon) keep one asap_sketchlib object per group, without the per-row Value wrapper.
  • Stored panes are immutable Arc<StateBatch>, read by reference.

Operators. Every physical operator kind gets a columnar implementation:

Operator Columnar implementation
Scan Matchers evaluated once per series id
TimeRange / TimeShift Selection on the timestamp column; instant lookback keeps the latest row per series
Filter, Project Vectorized expressions
Aggregate Cached series-to-group ids
SummaryAgg / SummaryMerge / SummaryEstimate / FinalizeExactAccumulator Whole state columns per call
Join / SemiJoin Hash on ids
Sort / Limit / TopK Permutation vector / heap

A node's output is computed once per evaluation and shared through Arc.

Per-sample updaters. A public API updates one open pane's state with one sample: (family, params) → updater, with update(group, value), merge and seal. It is the same code SummaryAgg uses on a batch, so a runtime's per-sample ingestion builds byte-identical state.

Thread safety. Batches and state columns are Send + Sync, so a runtime can share stored panes across query threads.

Scope and acceptance

  • Land it operator by operator. All existing executor tests, and the integration tests that execute plans, pass unchanged.
  • A parity test checks each per-sample updater against the batch SummaryAgg for every summary family.
  • Measured on the ASAPQuery-backend benefit harness, release build, absolute numbers:
    • pane construction approaches 2.3 µs/sample;
    • stored-state queries approach 4.1 / 24.8 / 33.5 ms on the same data;
    • the row-layout share of data-plane CPU drops well below the measured 68%.
  • Out of scope: the runtime's scheduling, threads and SDS. Those live in ASAPQuery-backend.

Related

ASAPQuery-backend design-doc PR #826, sections "Execution engine: a columnar DAG executor" and "Overhead of the data plane, compared with ASAPQuery". #761 (Stage 3 cost terms for scans, per-row work and merges), #755 (installable plan and RawSampleSelector).

Activity

  1. added
    bindingLogical-to-physical binding and implementation selection
    enhancementNew feature or request
    on Oct 19, 2026
  2. zzylol commented on Oct 19, 2026

    @zzylol
    ContributorAuthor

    Done in #763, #765, #766, #767, #768 and #769:

    • every physical operator runs on Arrow column batches;
    • the row form of Batch is removed;
    • state columns and the public per-sample StateColumnUpdater (HydraCms included) are in;
    • Batch::try_from_columns builds a pane from columns;
    • the compiled plan and RawSampleSelector are Send + Sync;
    • stored panes are charged once, by the store, not again by each query.

    be-micro, same machine, executor before the first PR vs main now:

    Before Now
    pane construction 10.7 µs/sample 5.7 µs/sample with the batch kept
    pane construction, per-sample updater — 1.2 µs/sample
    sum by (job) (rate) 15.7 ms 4.1 ms
    topk by (job) 15.8 ms 4.2 ms
    5m quantile 31.8 ms 21.1 ms
    60m quantile 64.9 ms 49.5 ms

    The remaining follow-up is a Low: StateColumnUpdater::add_group_with_key refuses an empty key, while the batch build accepts a group with no group columns. Flat sketch buffers are tracked in #764.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bindingLogical-to-physical binding and implementation selectionenhancementNew feature or requestresource

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions