Optimize decimal scale power calculation - #10567
Conversation
Replace 10_f64.powi(scale) with a bit-identical lookup table for valid decimal scales (0..=Decimal256Type::MAX_SCALE) in arrow-cast and parquet-variant-compute. - Add public decimal_f64_power helper with exhaustive bit-equality test - Use helper in decimal/float casts and Variant float-to-decimal conversion - Fall back to powi for negative/out-of-range scales Closes apache#10523
Follow-up verification (evidence)Pushed a tightened revision:
Local: cast tests 326 passed, clippy -D warnings clean, fmt clean, |
|
Does this make a measurable difference in any benchmarks? The |
|
run benchmark cast_kernels |
This comment was marked as duplicate.
This comment was marked as duplicate.
|
🤖 Arrow criterion benchmark completed (GKE) | trigger Instance: Comparing agent/10523-decimal-power-lookup (0aa845f) to 6b7d6b3 (merge-base) diff Run configurationrun benchmark cast_kernelsCPU Details (lscpu)Details
Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
this is a good point, i think we could achieve the same speedup via fixing the cast kernel to not have the power inside in the hot loop |
Document that only non-negative scales 0..=MAX_SCALE use the lookup table; negative/out-of-range scales intentionally fall back to powi. Split unit tests so LUT coverage and fallback coverage are explicit.
Re: measurable impact /
|
| Benchmark | PR | main | Ratio (main/PR) |
|---|---|---|---|
| cast decimal128 to float64 | 27.1±0.02µs | 27.1±0.02µs | 1.00 (tie) |
| cast decimal256 to float64 | 60.0±0.05µs | 68.5±0.04µs | 1.14 (PR faster) |
| cast float64 to decimal128(32, 3) | 34.4±0.05µs | 34.4±0.05µs | 1.00 (tie) |
So end-to-end cast kernels: no change on decimal128↔float, ~14% faster on decimal256→float64 in that run. That is consistent with “powi once per array” — gains show up where the one-time scale setup is a larger fraction of work (e.g. decimal256 path), not as a per-element win.
On Jefffrey’s follow-up (same speedup by ensuring power is not in the hot loop): fully agree as a kernel-structure goal. This PR only replaces the scale-power calculation with a LUT (issue #10523). If maintainers prefer closing #10523 as “not worth it” given mostly-neutral cast_kernels results, or want a follow-up that restructures the cast so scale is always outside any residual hot path, happy to adjust.
Review replies (ethantang93)
- Negative scales: intentionally no LUT →
powifallback (documented + tests split). - Test nit: split LUT vs fallback tests in latest push.
|
please do not copy paste LLM responses, it is very disrespectful; we are trying to have a discussion with the author of the PR not an LLM |
|
You are right. I used AI assistance to draft that reply, and the result was impersonal and inappropriate for a maintainer discussion. I am sorry. The end-to-end benchmark does not justify adding this lookup table, so I am closing the PR rather than consuming more review time. Thank you for running the benchmark and for the direct feedback. |
|
Closing based on the official cast benchmark: the measured end-to-end benefit is not broad enough to justify the added table and maintenance cost. |
Which issue does this PR close?
Rationale for this change
Decimal↔float casts recompute
10^scalewithf64::powi. Valid Arrow decimal scales are bounded byDecimal256Type::MAX_SCALE(76), so these powers can be looked up instead of recomputed.What changes are included in this PR?
10_f64.powi(0..=76)in one shared table.decimal_f64_power(scale)and use it in:arrow-castdecimal→float (single_decimal_to_float_lossy)arrow-castfloat→decimal (cast_floating_point_to_decimal)parquet-variant-computeVariant float→decimal (variant_to_unscaled_decimal)powifor negative or out-of-range scales (preserves prior behavior).10_f64.powifor every signed scale in±MAX_SCALE.Scope note (issue file list)
arrow-cast/src/cast/mod.rs10_f64.powiwith lookuparrow-cast/src/cast/decimal.rs10_f64.powiwith lookuparrow-arith/src/numeric.rs10_f64.powi; uses integerpow_checked/pow_wrappingon decimal natives (different path)arrow-cast/src/parse.rsf64::powi; uses integer powers for interval/decimal string parseparquet-variant-compute/.../type_conversion.rs10_f64.powi(scale)pattern — also convertedAre these changes tested?
Evidence from local validation on branch
agent/10523-decimal-power-lookup:Table bit-identity (standalone
rustccheck)Compared all 77 table entries to
10_f64.powi(i).to_bits()fori in 0..=76→bad=0 total=77.Unit test
cargo test -p arrow-cast test_decimal_f64_power_matches_powi --lib→ 1 passedCompares
.to_bits()for every scale in-(MAX_SCALE)..=MAX_SCALE.Cast suite
cargo test -p arrow-cast cast:: --lib→ 326 passed.Clippy
cargo clippy -p arrow-cast --all-targets --all-features -- -D warnings→ passed.Fmt
cargo fmt --all -- --check→ passed.Downstream compile
cargo check -p parquet-variant-compute→ passed (uses publicdecimal_f64_powerviaarrow::compute).Are there any user-facing changes?
powiresults).arrow_cast::cast::decimal_f64_power(also reachable viaarrow::computere-export). Pure function; no breaking change.AI assistance disclosure
Assisted generation/investigation was used; Jaideep Pyne is the human operator responsible for the submission. Changes were validated with the commands above.