Repository navigation
Push decimal comparisons, between, and fills through Widen - #11003
Conversation
Merging this PR will degrade performance by 12.91%1 warning
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
3f13e37 to
e4a453e
Compare
825399a to
956796c
Compare
956796c to
26f34a6
Compare
c626cfa to
86478c0
Compare
6742cd1 to
9b61103
Compare
5b902e2 to
4bd352b
Compare
Tracking Issue: #10701 Based on `develop`, including the writer work previously in #11002. #11003 and #10994 build on it. No release gate. ## Summary Canonical decimal width follows precision, so small values under `Decimal(38, 2)` still occupy 16 bytes each. A decimal `Widen` keeps those values in a lower-precision child while retaining the column's logical dtype. ## Changes The child precision must hold every valid value and have a narrower canonical width. Execution restores the logical width. Until the later kernels land, operations use that canonical fallback. Serialization writes a concrete decimal leaf through the existing `vortex.decimal` wire form without executing values or validity. Integer `Widen` arrays, non-decimal leaves, and nonserializable validity require explicit preparation by the caller. Its metadata stores the width, while the containing format supplies the parent precision. This keeps an `i32` child at four bytes per value, including IPC and writes without a compressor. A serialized-byte regression test checks that size independently of the eventual read encoding. <details> <summary>Historical local benchmark results</summary> These retained measurements predate this style pass and rebase. They do not measure the current head. `cargo bench -p vortex-array --bench widen_decimal`, Apple M4, medians. The numbers were measured at the top of this stack before the split, so they cover the whole stack, not this layer alone. The baseline is the canonical input, and the candidate is the same values as a `Widen` (child precision 5 under precision 10, 9 under 18, and 18 under 38). CodSpeed on this PR measures the layer itself. | bench | p10 canonical | p10 Widen | p18 canonical | p18 Widen | p38 canonical | p38 Widen | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | `filter` | 22.16 µs | 5.083 µs | 13.66 µs | 4.916 µs | 10.58 µs | 24.74 µs | | `execute` | 10.59 ns | 2.832 µs | 10.26 ns | 2.926 µs | 10.34 ns | 9.895 µs | `execute` on a `Widen` is the widening copy, which the canonical input does not need. The canonical `filter` medians at p10 and p18 were bimodal across runs, and `filter` at p38 is slower on the `Widen`. </details> ## API Changes `WidenArray::try_new` accepts decimal children. Adds `WidenArray::encode_decimal` and registers a writer-only `WidenPlugin`, whose only write ID is `vortex.decimal`. ## Validation Source review and rebase range-diffs only. No local tests, builds, linting, or documentation checks were run. A commit hook flagged one formatting wrap, corrected manually without rerunning the check. --------- Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
4bd352b to
758a37d
Compare
93dee65 to
ed42055
Compare
| #[divan::bench(args = wide_cases())] | ||
| fn compare_constant(bencher: Bencher, case: Case) { | ||
| let values = case.values(); | ||
| bench_case( | ||
| bencher, | ||
| || (values.array(case), case.constant(0)), | ||
| |(array, constant), ctx| execute_canonical(array.binary(constant, Operator::Lt), ctx), | ||
| ); | ||
| } | ||
|
|
||
| #[divan::bench(args = wide_cases())] | ||
| fn compare_constant_out_of_range(bencher: Bencher, case: Case) { | ||
| let values = case.values(); | ||
| bench_case( | ||
| bencher, | ||
| || { | ||
| ( | ||
| values.array(case), | ||
| case.constant(10i64.pow(u32::from(case.child_precision))), | ||
| ) | ||
| }, |
There was a problem hiding this comment.
do we actually need more benchmarks. You could unit test the pushdown?
joseph-isaacs
left a comment
There was a problem hiding this comment.
Code looks good.
I think we need less benchmark, tighter test.
Maybe assert of the reduction of a compare + widen
Compare decimal children at a common precision, narrow constants that fit, and answer out-of-range comparisons without widening the child values. Share the cast and execution paths between integer and decimal kernels. Decimal fills outside the child precision use canonical execution. Assert narrow comparison operand dtypes and concrete out-of-range results through the registered comparison kernel, including validity and null constants. Compare logical results with canonical decimal operations. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
ed42055 to
b991aa6
Compare
|
I removed the benchmarks |
Tracking Issue: #10701 Stacked on #11003. #11005 builds on it. No release gate. ## Summary Decimal widening preserves order, equality and validity, so ordered aggregates can use the child instead of copying it first. Sum also needs the logical result precision: using the child's sum dtype could overflow a result that should fit. ## Changes Ordered aggregates widen their child partials, and MinMax reuses exact cached child extrema before scanning. Decimal `Sum` runs in the logical sum dtype only when the legacy `Stat::Sum` path combines independent batch totals. `SumV2` and `Sum` without `skip_nans` keep the fallback because their running sum can cancel a batch total that would overflow on its own. Tests compare running partials against fresh canonical batches so cached statistics cannot hide kernel work. <details> <summary>Historical local benchmark results</summary> These retained measurements predate this style pass and rebase. They do not measure the current head. `cargo bench -p vortex-array --bench widen_decimal`, Apple M4, medians. The numbers were measured at the top of this stack before the split, so they cover the whole stack, not this layer alone. The baseline is the canonical input, and the candidate is the same values as a `Widen` (child precision 5 under precision 10, 9 under 18, and 18 under 38). CodSpeed on this PR measures the layer itself. | bench | p10 canonical | p10 Widen | p18 canonical | p18 Widen | p38 canonical | p38 Widen | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | `min_max` | 14.93 µs | 15.12 µs | 14.62 µs | 15.12 µs | 21.58 µs | 14.95 µs | `sum` medians are within 8% of the canonical input at every precision. </details> ## API Changes None. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
Tracking Issue: #10701
Based on
develop, including the merged #10946. #11004 builds on it. No release gate.Summary
Comparisons, bounds checks and fills otherwise widen every decimal value before doing their work. The child's precision already bounds its values, so these operations can often stay narrow or answer a comparison without reading the buffer.
Changes
Constants that fit use the child dtype, and constants outside its precision answer comparisons from the range bound. Two decimal children compare at their larger precision. Dtype matches select the compatible types, then integer and decimal kernels share casts and execution. A decimal fill outside the child precision falls back to canonical execution. These shortcuts rely on the documented requirement that every valid value fits the child precision.
Shares the canonical comparison assertion and decimal fixtures, removes duplicate scalar precision coverage, and reduces the new decimal test cases from 193 to 99. Keeps the four paired benchmark cases for in-range comparisons and the out-of-range shortcut.
Historical local benchmark results
These retained measurements predate this style pass and rebase. They do not measure the current head.
cargo bench -p vortex-array --bench widen_decimal, Apple M4, medians. The numbers were measured at the top of this stack before the split, so they cover the whole stack, not this layer alone. The baseline is the canonical input, and the candidate is the same values as aWiden(child precision 5 under precision 10, 9 under 18, and 18 under 38). CodSpeed on this PR measures the layer itself.compare_constantcompare_constant_out_of_rangeAPI Changes
None.
Validation
Source review and downstream rebase range-diffs. No local tests, builds, formatting, linting, or benchmarks were run, per repository instructions.