Repository navigation
Conversation
82c5dc9 to
7477dcb
Compare
Merging this PR will degrade performance by 19.67%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | density_sweep_dense_runs[0.001] |
28.5 µs | 47 µs | -39.26% |
| ❌ | Simulation | filter_powerlaw_by_mostly_true[250000] |
98.1 µs | 145.1 µs | -32.42% |
| ❌ | WallTime | filter_avx2[64] |
2.1 µs | 2.3 µs | -10.83% |
| ⚡ | WallTime | starts_with_avx512[8, VarBin] |
46.3 µs | 40.7 µs | +13.76% |
| 🆕 | Simulation | once[bool] |
N/A | 185.2 µs | N/A |
| 🆕 | Simulation | once[interleave] |
N/A | 520 µs | N/A |
| 🆕 | Simulation | once[list] |
N/A | 1.2 ms | N/A |
| 🆕 | Simulation | once[varbinview] |
N/A | 196.2 µs | N/A |
| 🆕 | Simulation | repeated[bool] |
N/A | 156.1 µs | N/A |
| 🆕 | Simulation | repeated[interleave] |
N/A | 547.4 µs | N/A |
| 🆕 | Simulation | repeated[list] |
N/A | 1.1 ms | N/A |
| 🆕 | Simulation | repeated[varbinview] |
N/A | 172.9 µs | N/A |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing jiengup:migrate-every-encoding (7477dcb) with develop (ed119bf)
2d7cff6 to
a906ba7
Compare
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
Signed-off-by: Gunther Xing <jiengup@gmail.com>
a906ba7 to
2f5121b
Compare
Summary
OperationsVTable::scalar_attoprobe_scalar#9971OperationsVTable::scalar_atcannot retain preparation between row reads. The probe API introduced in #9843 retains encoding state, validity, and child probes across repeated reads.This PR migrates 30 implementations to explicit
probe_scalarmethods: eight simple implementations, ten structural implementations, Map, Patched, PiecewiseSequence, ScalarFn, ALPRD, BitPacked, Delta, OnPair, ParquetVariant, Sparse, Zstd, and ZstdBuffers. The simple, structural, Map, ScalarFn, ParquetVariant, and Sparse implementations useProbeState = ()and reuse generic validity and child probes where needed. Patched retains its most recently accessed lane; PiecewiseSequence retains a bounded lookup index; ALPRD retains one patch query; BitPacked retains one validated block and its base boundary; Delta retains one decoded chunk; OnPair retains one row's code boundaries; Zstd retains one bounded decoded window; ZstdBuffers retains one decoded inner probe under a byte cap. Each migratedscalar_atdelegates to a one-off probe during the transition.The current scope is the sixteen commits in
origin/develop..HEAD, from18995b8d91throughcf5a9da2aa, based onorigin/developatd270200d65.Changes
Milestones
The branch contains separate commits for the simple, structural, Map, Patched, PiecewiseSequence, ScalarFn, ALPRD, BitPacked, Delta, OnPair, ParquetVariant, Sparse, Zstd, and ZstdBuffers milestones, plus a lint and formatting fix and a benchmark coverage commit. The array migrations are complete; the later API removals remain pending below.
probe_scalarrequired; deprecate and removeOperationsVTable::scalar_atArrayRefscalar and validity accessors on the later-release schedule in Migrate every encoding fromOperationsVTable::scalar_attoprobe_scalar#9971ScalarFnValidityis an additional internal target.TransposedBoolis listed in the issue but has no implementation in the current tree.Preparation and state
()means no encoding-owned cache; a repeated probe can still retain validity and child probes. Pending designs are markedTBD. Candidate reuse paths are listed to guide each milestone, not to commit to a cache before its benchmark and correctness review.ProbeState()()()()()()()()()()()()()()()()()()()PatchedProbeStatePiecewiseProbeStateVecExecutionArgs()ALPRDProbeStateBitPackedProbeStateDeltaProbeStateOnPairProbeState()()ZstdProbeStateZstdBuffersProbeStateStructural, Map, Patched, and PiecewiseSequence behavior
Structural implementations read children through
ProbeState::slot, so repeated parent reads retain the corresponding child probes. List, ListView, and FixedSizeList gather elements directly from the row's range. DecimalByteParts reconstructs the decimal with a bounded stack buffer for lower parts.Map reads its validated ListView child and wraps the returned entry values in the Map dtype. It preserves null and empty maps without requiring the entry array to execute as a physical Struct array.
Patched retains the last lane's range. Primitive lane offsets and patch indices use direct buffer access. For non-Primitive indices, a repeated visit to the cached lane decodes and retains at most 64 indices; larger lanes continue probing without that decoded cache. One-off reads do not allocate an encoding-owned cache. Patch lookup accounts for the array's slice offset and preserves the parent validity and logical dtype.
PiecewiseSequence reads only the selected start and multiplier through child probes. Constant lengths use direct division; other lengths use a sparse cumulative-end index only for repeated probes. The index has at most 4,096 checkpoints (about 64 KiB of checkpoint entries) regardless of the number of pieces or reads, plus a last-piece hint. A Primitive lengths child is scanned directly; other encodings are decoded in blocks of at most 256 piece lengths during lookup. One-off reads retain no index. Empty pieces, later unused pieces, and deferred range errors preserve the point-read behavior.
ScalarFn reads arguments through stable child slots. Each row's argument values still become length-one Constant arrays and use the existing
VecExecutionArgsadapter. It retains no encoding-owned state because the argument values and results belong to the current row. Its computed scalar supplies nullness; a parent validity precheck could recurse throughScalarFnValidity. Both Constant and Canonical function results keep the prior handling.ALPRD checks its parent validity, searches exception positions through stable child probes, then reads either the patch value or dictionary code and combines it with the low bits.
Patches::search_indexand the new probe-aware search share the slice and chunk-offset range calculation. For repeated reads of the same patch-bearing row,ALPRDProbeStatekeeps only the most recent row and its patch hit or miss; it begins retaining after a second consecutive read. The state has constant size, and one-off reads retain nothing.BitPacked checks parent validity before searching patches. Patch hits return a scalar cast to the parent dtype and do not validate packed bytes. Global-width misses use the existing unpacker; blocked-width misses probe only the selected block's offsets and validate that block before unpacking.
BitPackedProbeStateretains the last validated block's byte range and the first offset for encoded offset children. Its size is constant. A patch-query cache was tried and removed after a sustained small random-read regression.Delta checks parent validity on every read. A cache miss decodes only the selected physical 1,024-value chunk through the existing full-chunk algorithm, preserving signed wraparound and deferred child errors. A repeated probe retains that chunk, bounded to at most 8 KiB of numeric values. The cache key combines the caller index with the sliced array's physical offset; nulls do not decode a chunk.
OnPair checks outer validity before accessing children or initializing the dictionary. Primitive row offsets and lengths are read directly; other encodings use stable child slots, allowing their own probe states to persist. The existing shared dictionary cache remains the sole dictionary cache. A repeated probe retains only one row's code boundaries, reusing both on a same-row read or the end boundary for the next row. For at most four compressed codes, child probes beat per-row materialization in the measured short-row fixture; longer runs keep bulk row decoding. Returned strings still own their bytes.
ParquetVariant checks outer validity once and reads metadata, typed values, and raw values through stable child slots. Typed values take precedence; struct typed values merge with raw object fields; a missing typed value falls back to raw bytes. Reconstruction is per-row, so its encoding-owned state remains
().Sparse searches for a patch once through
Patches::search_index_with_probes, then reads a hit through the patch-values slot or clones the fill scalar. Both alternatives already carry nullness, so it avoids a redundant validity-array patch search. Slice offsets and optional chunk offsets stay in the sharedPatchessearch helper. It retains no encoding-owned state.VarBin directly reads already-hosted Primitive offsets and keeps child probes for other offset encodings. The direct path was added after the final baseline comparison found a 7.3% repeated-read regression in the common Primitive-offset fixture; the final path is 33.5% faster than
origin/developin that fixture.Zstd checks outer validity and uses the existing rank-aware frame decompressor. One-off reads decode one row. Repeated reads decode one row on the first window miss, then promote a 128-row aligned window only after a second request in it. The state retains one decoded window only when its decompressed frame bytes fit within 1 MiB; an oversized window continues on the one-row path. This preserves random-read performance while reusing frame decompression for localized reads.
ZstdBuffers checks outer validity, then reconstructs and retains one decoded inner array and its
RepeatedArrayProbewhen the sum of its uncompressed top-level buffers is at most 1 MiB. The inner probe keeps any inner encoding state across reads. Larger wrapped arrays and one-off reads use the existing decode path without a retained inner array.Tests and benchmarks
test_scalar_probe_consistencyfor encodings outside the full compute conformance suite, checking one-off and repeated reads, nullness, revisits, and slices.probebenchmark with Bool, VarBinView, List, Interleave, Map, Patched, PiecewiseSequence, and ScalarFn, alongside the existing Primitive, Dict, and Struct cases. Adds Patched same-lane reads, PiecewiseSequence same-piece reads, and ScalarFn same-row reads, each in one-off and repeated modes.alp_rd_probefor f32/f64, with and without exceptions, using random and same-row one-off/repeated reads and explicit patch-hit/miss rows.bitpacked_probefor global widths and blocked Primitive/FoR offsets, with and without patches, using random and same-block one-off/repeated reads and explicit patch-hit/miss rows.delta_probefor Primitive and BitPacked children and nullable signed values, comparing random, same-chunk, and two-chunk alternating reads in one-off and repeated modes.probefor short and long strings, Primitive and recursively compressed children, and random, sequential, and same-row one-off/repeated reads.probefor typed, raw, and mixed rows under random and same-row one-off/repeated reads.probefor non-null and nullable values under random, same-patch, and same-fill one-off/repeated reads.zstd_probefor Primitive, nullable Primitive, and Utf8 values under random, same-frame, and same-row one-off/repeated reads.zstd_buffers_probefor Primitive, nullable Primitive, and Utf8 inner arrays under random and same-row one-off/repeated reads.probebenchmark to cover Constant, Decimal, Null, Filter, FixedSizeList, ListView, Union, Variant, VarBin, and the internal ScalarFnValidity array. Adds dedicatedprobebenchmarks for ByteBool, Sequence, and DecimalByteParts.Verification
cargo nextest run -p vortex-array -p vortex-bytebool -p vortex-sequence: 4,201 passed, 1 skipped.cargo nextest run -p vortex-python: 35 passed.API Changes
Uses the probe API already introduced in #9843. The migrated implementations retain
scalar_atas a compatibility shim. Makingprobe_scalarrequired, removingOperationsVTable::scalar_at, and changing the public scalar/validity accessors remain pending under #9971. PythonArray scalar access remains unsupported.AI Usage
Final point-read comparison to baseline
All the results below are obtained only on a development machine (macOS with Apple Silicon). Not representative of production performance.
The baseline is the implementation at
origin/developcommitd270200d65; The benchmark source files, benchmark-only Cargo manifests, andCargo.lockwere copied from the migrated branch into that source tree, so both sides used the same fixtures and dependency versions. Each command usedcargo bench -p <crate> --bench <name> -- --min-time 0.1 --sample-count 15on the same machine with Rust 1.99.1. Values are Divan medians in microseconds per iteration. Each iteration uses one retained probe for the indicated number of scalar reads.Localized repeated reads show the benefit of each bounded cache. This table uses 256 reads per iteration except the two Zstd rows, which use 64.
The VarBin row uses the final Primitive-offset fast path from
cf5a9da2aa; its first forwarding implementation measured 15.33 µs repeated and 17.49 µs one-off, versus 14.29 µs and 16.66 µs on the baseline. Direct host offset reads reduced the final repeated median to 9.499 µs and the one-off median to 10.74 µs. The Variant constant-core fixture remained 6.8% slower for repeated reads; its one-off median improved from 7.332 to 6.541 µs. A trial of parent validity throughProbeState::is_validdid not improve Variant repeated reads and was reverted. Zstd random reads are unchanged in the Primitive fixture, while same-frame reads benefit from its bounded window. These are microbenchmarks over the listed fixtures; the per-milestone sections above give the broader access-pattern results.Benchmark commands compiled and completed for both source trees. Focused tests written for the final four encodings and Rust lint/format checks were not run under the repository’s user-chosen verification policy. Markdown documentation stays outside the commits.