Brief description
Remove the extra whole-input passes used to validate flat matches tape widths in aggregation, comparison, and index-builder kernels, while preserving the existing deterministic ValueError behavior for undersized tapes.
The current ensure_tape_width helper is O(1), but its callers first compute expected_matches_width by scanning every row's start/end range. There are currently 49 such prepass sites across 27 source files. The kernel then traverses the same row metadata again to do the actual work.
This is most likely to matter for sparse workloads, zero-width rows, or otherwise cheap per-row operations, where the validation pass can become a material fraction of total runtime.
ELI5
Before processing the tickets, the code reads every ticket once to count how much tape will be needed. It then starts over and reads all the tickets again to do the work. Instead, each ticket can check that enough tape remains immediately before using its own section.
Proposed direction
- Prototype fused validation in one representative
*_matches core with existing benchmark coverage.
- For each row, derive its validated tape width using the same range/index rules as the processing loop.
- Before indexing
matches, use checked arithmetic to verify that the row's required end position is within matches.len().
- Return the same clear Python
ValueError when the tape is too short; do not replace validation with panics, unchecked indexing, silent truncation, or a caller-only contract.
- If benchmarks show a meaningful improvement or neutrality, extract a small shared helper and roll the pattern out to the remaining aggregation, comparison, and index-builder sites without obscuring their hot loops.
Acceptance criteria
- Valid-call output, ordering, dtype behavior, sentinel handling, and null-mask behavior remain unchanged.
- A tape that is too short still raises a deterministic
ValueError before any out-of-bounds access.
- Checked addition prevents
n + row_width overflow.
- Tests cover exact-length, longer-than-required, one-element-short, invalid/sentinel ranges, zero-width rows, and a malformed row followed by a valid row.
- Benchmarks compare the current prepass and fused-validation implementations for dense, sparse, and zero-width-heavy fixtures at small and large row counts.
- The change is rolled out beyond the prototype only if benchmarks are neutral or positive.
cargo test --no-default-features, clippy, formatting, and benchmark compilation pass.
Scope notes
Brief description
Remove the extra whole-input passes used to validate flat
matchestape widths in aggregation, comparison, and index-builder kernels, while preserving the existing deterministicValueErrorbehavior for undersized tapes.The current
ensure_tape_widthhelper is O(1), but its callers first computeexpected_matches_widthby scanning every row's start/end range. There are currently 49 such prepass sites across 27 source files. The kernel then traverses the same row metadata again to do the actual work.This is most likely to matter for sparse workloads, zero-width rows, or otherwise cheap per-row operations, where the validation pass can become a material fraction of total runtime.
ELI5
Before processing the tickets, the code reads every ticket once to count how much tape will be needed. It then starts over and reads all the tickets again to do the work. Instead, each ticket can check that enough tape remains immediately before using its own section.
Proposed direction
*_matchescore with existing benchmark coverage.matches, use checked arithmetic to verify that the row's required end position is withinmatches.len().ValueErrorwhen the tape is too short; do not replace validation with panics, unchecked indexing, silent truncation, or a caller-only contract.Acceptance criteria
ValueErrorbefore any out-of-bounds access.n + row_widthoverflow.cargo test --no-default-features, clippy, formatting, and benchmark compilation pass.Scope notes
ensure_equal_lengthsand comparison-op decoding unchanged.reorder_indexvalidation and allocation work remains under [MAINT] Harden and simplify conditional-join index builders #25.