Repository navigation
Decode DecimalByteParts into the canonical array or a decimal Widen - #10905
connortsui20 wants to merge 4 commits into
Conversation
Merging this PR will degrade performance by 57.42%1 warning
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
59eca0e to
881fe73
Compare
881fe73 to
fca52f8
Compare
|
So the regressions are definitely real, but without narrow decimal encoding there's not much we can do. I will note though that I think it is good we regress these benchmarks because now it actually shows how expensive a very large decimal is in its fully decoded representation. |
fca52f8 to
e0640ce
Compare
|
But we have Widen now so shouldn't this decode to Decimal(Widen) to preserve behaviour? |
e0640ce to
5b77c67
Compare
5b77c67 to
ed4f730
Compare
WD1 to WD4, CD3, CD3b and the decimal Interleave kernel merged, and CD4, CD5 and WD6 are open drafts. The plan now lists CD3b (#10939) and the Interleave kernel (CD4p, #10940) as dependencies of CD4, and CD3b as a dependency of CD6 and CD7. CD7 merges right after CD5 (#10905). D26 moves to CD6, because CD4 (#10946) adds no serialization lowering: the default writer executes a Widen, and reads of old files are the first ordinary source of one. The CD6 and WR1 briefs now carry that choice. D35 no longer makes WD6 wait for WD5. #10908 joins the follow-up issues, and the plan suggests settling the mechanism of Signed-off-by: Connor Tsui <connor.tsui20@gmail.com> #10748 before WD5.
The first version of #10905 widened every narrow DecimalByteParts MSP with a copy, and CodSpeed showed regressions of up to 11x. The maintainer approved a plan on 2026-10-29: decoding returns the canonical array or a decimal Widen, LT2 becomes part of CD5, and CD5 depends on CD4. D9 now has one rule for every stored width, so a wider old node can also become a Widen. D16, D21 and the new D40 make WidenArray::from_stored_decimal the single owner of the choice between the canonical array and a Widen. D26 is settled: CD4 lowers a Widen in the serializer. D31 names 0.91.0, and D36 records the decimal Sum and MinMax kernels. D41 records the order and the stacks, with two new PRs, and D42 the decode benchmarks that stop at a Widen. The implementation plan follows: the PR table, the graph, the waves, the later work, the rules for every PR and the risks. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
ed4f730 to
86ce007
Compare
`widen_values` and `widened_buffer` widened each value with a checked cast and an `expect`, and collected through `BufferMut::extend_iter`, whose write loop has two exits. LLVM vectorized some width pairs, such as `i32` to `i64`, but left others scalar, such as `i64` to `i128`. Build the buffer with `from_trusted_len_iter`, like the Arrow exporter, and fall back to zero instead of panicking. The fallback is dead code when the target is at least as wide as the source, so the loop has one exit and no panic edge. `widened_buffer` now calls `widen_values`. On Apple M4, `i64` to `i128` is about 1.75x faster and `i32` to `i64` is unchanged, but widening into `i256` is 1.24x to 1.7x slower, for a reason we did not find. That widening only happens at precision 39 to 76, and once #10946 and #10905 land, most such columns decode to a `Widen`, so one loop for every width is the simpler trade. A test checks every widening pair against a per-value cast. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
022c6d5 to
cd8347d
Compare
cd8347d to
fe6875a
Compare
fe6875a to
2720140
Compare
2720140 to
e25d528
Compare
11f665c to
ee2eb96
Compare
ee2eb96 to
5092c84
Compare
5092c84 to
c8c52ef
Compare
c8c52ef to
4c2b19b
Compare
4c2b19b to
b6a0182
Compare
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
|
I have been redesigning this, so this is no longer the change I want to make. |
Summary
Every decimal leaf must have the canonical width of its precision, but DecimalByteParts usually stores its most significant part (MSP) narrower than that. Copying the MSP to the canonical width makes every read pay for the copy: an earlier version of this PR did that, and CodSpeed showed
decode[msp_i32_p18_nullable]going from 18.7 µs to 218.8 µs. Decoding to a decimalWidenkeeps reusing the narrow MSP buffer, asdevelopdoes today.Changes
The child precision comes from the values through
WidenArray::from_stored_decimal, which thevortex.decimalreader (#10995) also uses, because a width does not determine a precision (32,767 fits in ani16but needs precision 5), and a child labeled too low breaks compare shortcuts and casts. Known cost: without exact cached MSP statistics, decoding makes one pass to find the range. It uses the integermin_maxon the MSP, since the decimal range scan is not vectorized, and the child caches the result for later aggregates.Bounding the lower parts means assembly never needs a narrowing copy, and no file breaks, since no edition permits lower parts yet.
CUDA has no
Widenkernel, so the GPU still widens a narrow MSP and rejects a wider one, because the overflow check would need a host sync. This needs a CUDA reviewer.Merge #10992 right after this.
Local benchmark results from an earlier revision
The decode benchmarks stop at the
Widento measure the read path, and the newcanonicalizebenchmark measures the widening copy that consumers of the canonical array pay.cargo bench -p vortex-decimal-byte-parts --bench dbp_computeon an Apple M4, 8,192 rows, medians. The baseline is the tip of #10946 before its split, which decoded DecimalByteParts asdevelopdoes. The candidate is a revision of this PR from before the cleanup and rebase, so these numbers do not measure the current head.msp_i64_p18_canonicalis the control, because its MSP already has the canonical width. Benchmarks that this PR does not change, such ascompare_constant, ran about 20% slower in the candidate run, so part of each difference is run-to-run noise.decodegap_msp_i16_p10decodemsp_i32_p18decodemsp_i32_p18_nullabledecodemsp_i64_p18_canonicalcanonicalize(new)gap_msp_i16_p10canonicalize(new)msp_i32_p18canonicalize(new)msp_i32_p18_nullablefiltergap_msp_i16_p10filtermsp_i32_p18filtermsp_i32_p18_nullablefiltermsp_i64_p18_canonicalmin_maxgap_msp_i16_p10min_maxmsp_i32_p18min_maxmsp_i32_p18_nullableAPI Changes
DecimalByteParts decoding returns a canonical decimal array or a decimal
Wideninstead of a narrow leaf. The constructors and the v2 reader reject lower parts that are notu64or that assemble wider than the canonical width.