You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue is mostly a WIP since I still need to figure out if the mechanics can be optimize further.
Design
A RowFn supplies typed row kernels. A blanket ScalarFnVTable implementation and private lifting layer turn those kernels into columnar execution. There is no separate public strict-function vtable.
The lifting owns the parts that should not be reimplemented by every function:
Read arity, dense safety, decode behavior, and fallibility from the RowFn witnesses. Run dispatch to validate the arguments and derive the result dtype from the visited OutputElement or OutputSink, then widen it when any input is nullable.
Short-circuit a null constant to an all-null result, or evaluate an entirely constant call once and broadcast it.
Decode each input once. A partially constant argument is decoded as one row and read with stride 0.
Derive the strict input validity and choose how the kernel sees rows behind nulls.
Execute the row kernel or an encoding-aware reduction, then reconcile the output dtype and apply validity.
Null handling has two contracts. Dense may evaluate payloads behind null rows and mask the result, so it is only available when every input element is DENSE_SAFE and execution is infallible. Filter guarantees that the row computation only sees valid rows.
Adaptive execution
For a mixed validity mask under Filter, the lifting chooses between two mechanisms per batch:
Branch-and-skip decodes the original columns, computes only set rows, fills skipped output slots with placeholders, and masks the result. This avoids filtering and scattering and preserves the input encodings.
Filter-and-scatter filters every input to the valid rows, executes densely, and scatters the result back. This is the fallback when null-tolerant decoding is unavailable, and can win when filtering avoids substantial per-row decode work.
The choice is invisible to the RowFn. InputElement::decode_null_tolerant first determines whether branch-and-skip is sound for the concrete arrays. DECODE_SHRINKS_WHEN_FILTERED then tells the selector whether filtering can avoid substantial decode work. If branch execution is unavailable for any reason, the batch falls back to filter-and-scatter.
The current rule always prefers branch-and-skip for bulk decodes. For a per-row decode, it filters when fewer than 75% of rows survive. This matches the faster forced strategy across the current byte_length and geo contains measurements, but it is still an experimental global threshold rather than part of the RowFn contract.
Even a Dense kernel may eventually benefit from skipping all-null mask words while keeping contiguous all-valid runs dense. That needs a skipped-row output contract and benchmarks for mask shape as well as surviving fraction.
Sink-backed execution initially remains on the dense and filter paths because a sink has no skipped-row representation.
Constant decoding and constant computation are separate. The machinery detects batch constants, decodes them once, and exposes their values to visit_prepared. The function still decides which work can be hoisted and computes that state itself.
Steps
Derive the ScalarFnVTable arity, strictness, fallibility, validity, and options serialization from RowFn, and derive its result dtype through dispatch.
Implement null-constant and all-constant lifting, dense execution, filtering, scattering, and output dtype reconciliation.
Implement branch-and-skip with null-tolerant decoding and output placeholders.
Add adaptive per-batch selection, forced-strategy test controls, and bytes/geometry crossover benchmarks.
Preserve encoding-aware reductions across dense, filtered, and branch-and-skip execution.
Validate the machinery with primitive, bytes, tensor, sink, fallible, and geometry functions.
Add before-and-after benchmarks for representative constant, nullable, and ordinary row workloads.
Unresolved questions
Does sink-backed execution need branch-and-skip before this work is complete, or are dense and filter sufficient for the initial API?
What benchmark set and regression threshold are required before replacing an existing hand-written implementation?
Non-blocking follow-ups:
Avoid probing reduce_encoded twice when branch execution is unsupported.
Revisit the global 75% threshold when another element with substantial per-row decode work exists.
Allow the fallible branch loop to stop iterating immediately after the first error.
This is a tracking issue for the private machinery that executes a
RowFnover Vortex arrays.Parent Epic: #9128
Related API tracking issue: #9129
This issue is mostly a WIP since I still need to figure out if the mechanics can be optimize further.
Design
A
RowFnsupplies typed row kernels. A blanketScalarFnVTableimplementation and private lifting layer turn those kernels into columnar execution. There is no separate public strict-function vtable.The lifting owns the parts that should not be reimplemented by every function:
RowFnwitnesses. Rundispatchto validate the arguments and derive the result dtype from the visitedOutputElementorOutputSink, then widen it when any input is nullable.Null handling has two contracts.
Densemay evaluate payloads behind null rows and mask the result, so it is only available when every input element isDENSE_SAFEand execution is infallible.Filterguarantees that the row computation only sees valid rows.Adaptive execution
For a mixed validity mask under
Filter, the lifting chooses between two mechanisms per batch:The choice is invisible to the
RowFn.InputElement::decode_null_tolerantfirst determines whether branch-and-skip is sound for the concrete arrays.DECODE_SHRINKS_WHEN_FILTEREDthen tells the selector whether filtering can avoid substantial decode work. If branch execution is unavailable for any reason, the batch falls back to filter-and-scatter.The current rule always prefers branch-and-skip for bulk decodes. For a per-row decode, it filters when fewer than 75% of rows survive. This matches the faster forced strategy across the current
byte_lengthand geocontainsmeasurements, but it is still an experimental global threshold rather than part of theRowFncontract.Even a
Densekernel may eventually benefit from skipping all-null mask words while keeping contiguous all-valid runs dense. That needs a skipped-row output contract and benchmarks for mask shape as well as surviving fraction.Sink-backed execution initially remains on the dense and filter paths because a sink has no skipped-row representation.
Constant decoding and constant computation are separate. The machinery detects batch constants, decodes them once, and exposes their values to
visit_prepared. The function still decides which work can be hoisted and computes that state itself.Steps
ScalarFnVTablearity, strictness, fallibility, validity, and options serialization fromRowFn, and derive its result dtype throughdispatch.Unresolved questions
Non-blocking follow-ups:
reduce_encodedtwice when branch execution is unsupported.Implementation history
None yet.