Skip to content

Decimal Mul/Div kernels - #9221

Open
mhk197 wants to merge 7 commits into
mk/decimal-scalar-mul-divfrom
mk/decimal-mul-div
Open

Decimal Mul/Div kernels#9221
mhk197 wants to merge 7 commits into
mk/decimal-scalar-mul-divfrom
mk/decimal-mul-div

Conversation

@mhk197

@mhk197 mhk197 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Adds native Mul and Div execution for decimal arrays, completing the operator set that #8724 started with Add/Sub. Both operators were previously rejected with numeric operator {op} is not yet supported for decimal arrays.

Stacked on #8888 — base is mk/decimal-scalar-mul-div, so review that one first. #8888 defines the Arrow decimal arithmetic rules (scalar::decimal_numeric_result_dtype) and implements them for scalars; this PR implements the array kernels against the same function rather than restating the formulas, so the scalar and array paths cannot drift:

operator result precision result scale
Add, Sub p + 1 s
Mul 2p + 1 2s
Div p + s + 4 s + 4

Execution

Lanes run at a working width from decimal_numeric_work_dtype, then narrow to the result's own storage width. The two widths are not always the same: Mul's intermediate is the result itself, but Div scales the dividend by 10^result_scale before dividing and so needs room for p + |result_scale| digits. A negative result scale scales the divisor instead.

DecimalOpConstants<W> hoists the per-execution constants — the result-precision bounds and the two division scale factors — out of the lane loop.

Every lane is checked at the working width. DecimalArray does not validate its stored values against the declared precision, so an out-of-precision value can reach a kernel and must not be able to overflow it; test_decimal_value_outside_working_width_errors has depended on that behaviour since #8724. Overflowing the result precision on a valid lane is an error, as is division by zero; invalid lanes never error.

Notes for review

  • The kernel is written against the checked_lanes / LaneZip API from Unify numeric compute kernels with vortex-compute iteration functions #8939 rather than the index-based loops this branch originally used, so the previous unsafe { get_unchecked } lane access is gone.
  • An earlier revision of this branch had a GuaranteedDecimalMul fast path that skipped the checked multiply and the bounds compare when 2p <= MAX_PRECISION. That was unsound — it assumed in-precision inputs, which nothing enforces — and is removed. It could return a value past the declared result precision, and could overflow the working width outright (panicking in debug, wrapping in release) on a value DecimalArray happily stores. Both cases are pinned by regression tests.
  • The conformance suite (compute::conformance::binary_numeric) now exercises Mul and Div for every decimal encoding, not just Add/Sub.

Checks

  • cargo nextest run -p vortex-array — 3200 passed
  • cargo nextest run --workspace — 7408 passed, 560 skipped
  • cargo clippy -p vortex-array --all-targets
  • cargo +nightly fmt --all --check

Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197 mhk197 added the changelog/feature A new feature label Aug 5, 2026
@codspeed-hq

codspeed-hq Bot commented Aug 5, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 0.68%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 1893 untouched benchmarks
🆕 4 new benchmarks
⏩ 43 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation compress_fsst[(10000, 64, 8)] 9.6 ms 10.8 ms -10.92%
Simulation chunked_dict_primitive_canonical_into[u32, (1000, 100, 100)] 1.6 ms 1.5 ms +10.74%
🆕 Simulation mul_decimal_i64_nonnull N/A 1.5 ms N/A
🆕 Simulation div_decimal_i128_nullable N/A 13.1 ms N/A
🆕 Simulation div_decimal_i64_nonnull N/A 3.6 ms N/A
🆕 Simulation mul_decimal_i128_nullable N/A 5.3 ms 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 mk/decimal-mul-div (5087468) with mk/decimal-scalar-mul-div (a2ecde0)

Open in CodSpeed

Footnotes

  1. 43 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.

@mhk197 mhk197 linked an issue Aug 5, 2026 that may be closed by this pull request
mhk197 added 6 commits August 5, 2026 16:00
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197
mhk197 marked this pull request as ready for review August 6, 2026 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/feature A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decimal Arithmetic

2 participants