Skip to content

Decode DecimalByteParts into the canonical array or a decimal Widen - #10905

Closed
connortsui20 wants to merge 4 commits into
developfrom
ct/dbp-canonical-decode
Closed

connortsui20 wants to merge 4 commits into
developfrom
ct/dbp-canonical-decode

Conversation

@connortsui20

@connortsui20 connortsui20 commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

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 decimal Widen keeps reusing the narrow MSP buffer, as develop does today.

Changes

The child precision comes from the values through WidenArray::from_stored_decimal, which the vortex.decimal reader (#10995) also uses, because a width does not determine a precision (32,767 fits in an i16 but 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 integer min_max on 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 Widen kernel, 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 Widen to measure the read path, and the new canonicalize benchmark measures the widening copy that consumers of the canonical array pay.

cargo bench -p vortex-decimal-byte-parts --bench dbp_compute on an Apple M4, 8,192 rows, medians. The baseline is the tip of #10946 before its split, which decoded DecimalByteParts as develop does. 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_canonical is the control, because its MSP already has the canonical width. Benchmarks that this PR does not change, such as compare_constant, ran about 20% slower in the candidate run, so part of each difference is run-to-run noise.

bench case baseline candidate
decode gap_msp_i16_p10 82.75 ns 1.29 µs
decode msp_i32_p18 73.97 ns 1.03 µs
decode msp_i32_p18_nullable 80.49 ns 5.332 µs
decode msp_i64_p18_canonical 72.02 ns 89.28 ns
canonicalize (new) gap_msp_i16_p10 2.519 µs
canonicalize (new) msp_i32_p18 2.041 µs
canonicalize (new) msp_i32_p18_nullable 6.957 µs
filter gap_msp_i16_p10 749.7 ns 1.791 µs
filter msp_i32_p18 1.041 µs 2.04 µs
filter msp_i32_p18_nullable 2.582 µs 6.291 µs
filter msp_i64_p18_canonical 1.999 µs 2.249 µs
min_max gap_msp_i16_p10 5.124 µs 1.958 µs
min_max msp_i32_p18 4.083 µs 1.739 µs
min_max msp_i32_p18_nullable 6.165 µs 5.957 µs

API Changes

DecimalByteParts decoding returns a canonical decimal array or a decimal Widen instead of a narrow leaf. The constructors and the v2 reader reject lower parts that are not u64 or that assemble wider than the canonical width.

@connortsui20 connortsui20 added the changelog/break A breaking API change label Oct 27, 2026
@connortsui20 connortsui20 mentioned this pull request Oct 27, 2026
20 of 34 tasks
@codspeed

codspeed Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 57.42%

1 warning

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

❌ 14 regressed benchmarks
✅ 1756 untouched benchmarks
🆕 4 new benchmarks
⏩ 47 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ CPU Simulation decode[msp_i32_p18_nullable] 28.2 µs 235.5 µs -88.04%
❌ CPU Simulation decode[msp_i64_p38] 23.1 µs 141.1 µs -83.65%
❌ CPU Simulation decode[gap_msp_i16_p10] 23 µs 129.6 µs -82.25%
❌ CPU Simulation decode[msp_i32_p18] 23.2 µs 109.1 µs -78.76%
❌ CPU Simulation sum[gap_msp_i16_p10] 83.3 µs 173.6 µs -52%
❌ CPU Simulation sum[msp_i32_p18_nullable] 162.9 µs 331.3 µs -50.84%
❌ CPU Simulation filter[msp_i32_p18_nullable] 154.3 µs 277.3 µs -44.36%
❌ CPU Simulation filter[gap_msp_i16_p10] 98 µs 170.1 µs -42.37%
❌ CPU Simulation sum[msp_i32_p18] 97.3 µs 152.2 µs -36.08%
❌ CPU Simulation filter[msp_i32_p18] 119.6 µs 175 µs -31.67%
❌ CPU Simulation sum[msp_i64_p38] 145 µs 208.2 µs -30.38%
❌ CPU Simulation min_max[gap_msp_i16_p10] 108.5 µs 151.6 µs -28.44%
❌ CPU Simulation filter[msp_i64_p38] 162.3 µs 224.1 µs -27.57%
❌ CPU Simulation decompress[dbp_bp_i32] 61 µs 83.3 µs -26.85%
🆕 CPU Simulation canonicalize[gap_msp_i16_p10] N/A 205.5 µs N/A
🆕 CPU Simulation canonicalize[msp_i32_p18_nullable] N/A 340.2 µs N/A
🆕 CPU Simulation canonicalize[msp_i32_p18] N/A 186.1 µs N/A
🆕 CPU Simulation canonicalize[msp_i64_p38] N/A 332 µ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 ct/dbp-canonical-decode (490ca09) with develop (068d819)

Open in CodSpeed

Footnotes

  1. 47 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from 59eca0e to 881fe73 Compare October 27, 2026 22:08
@connortsui20
connortsui20 changed the base branch from ct/decimal-width-owner to ct/decimal-scalar-factory-width October 27, 2026 22:08
@connortsui20
connortsui20 added this pull request to stack #10904 October 27, 2026 22:08
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from 881fe73 to fca52f8 Compare October 27, 2026 22:18
@connortsui20

connortsui20 commented Oct 28, 2026 •

Copy link
Copy Markdown
Member Author

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.

@connortsui20
connortsui20 marked this pull request as ready for review October 28, 2026 13:49
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from fca52f8 to e0640ce Compare October 28, 2026 15:39
@robert3005

Copy link
Copy Markdown
Contributor

But we have Widen now so shouldn't this decode to Decimal(Widen) to preserve behaviour?

@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from e0640ce to 5b77c67 Compare October 28, 2026 15:54
@connortsui20
connortsui20 removed this pull request from stack #10904 October 28, 2026 15:59
Base automatically changed from ct/decimal-scalar-factory-width to develop October 28, 2026 16:19
@github-actions
github-actions Bot temporarily deployed to docs-preview/pr-10905 October 28, 2026 16:29 Inactive
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from 5b77c67 to ed4f730 Compare October 28, 2026 18:07
@connortsui20
connortsui20 marked this pull request as draft October 29, 2026 14:40
connortsui20 added a commit that referenced this pull request Oct 29, 2026
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.
connortsui20 added a commit that referenced this pull request Oct 29, 2026
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>
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from ed4f730 to 86ce007 Compare October 29, 2026 19:35
connortsui20 added a commit that referenced this pull request Oct 29, 2026
`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>
@connortsui20
connortsui20 changed the base branch from develop to ct/decimal-widen October 29, 2026 19:40
@connortsui20
connortsui20 added this pull request to stack #10993 October 29, 2026 19:40
@connortsui20 connortsui20 changed the title Decode DecimalByteParts at the canonical decimal width Decode DecimalByteParts into the canonical array or a decimal Widen Oct 29, 2026
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from 022c6d5 to cd8347d Compare November 2, 2026 19:24
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from cd8347d to fe6875a Compare November 2, 2026 19:52
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from fe6875a to 2720140 Compare November 2, 2026 20:25
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from 2720140 to e25d528 Compare November 2, 2026 21:29
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch 2 times, most recently from 11f665c to ee2eb96 Compare November 3, 2026 13:36
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from ee2eb96 to 5092c84 Compare November 3, 2026 14:52
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from 5092c84 to c8c52ef Compare November 3, 2026 15:12
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from c8c52ef to 4c2b19b Compare November 3, 2026 16:31
@connortsui20
connortsui20 force-pushed the ct/dbp-canonical-decode branch from 4c2b19b to b6a0182 Compare November 3, 2026 21:25
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>
@connortsui20

Copy link
Copy Markdown
Member Author

I have been redesigning this, so this is no longer the change I want to make.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/break A breaking API change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants