mbutrovich opened a new issue, #12167:
URL: https://github.com/apache/arrow-rs/issues/12167

   ### Is your feature request related to a problem or challenge?
   
   Part of #11421.
   
   `i128` and `u128` division calls `__divti3` or `__udivti3` for every value, 
even with a literal divisor such as 10^18 (rustc 1.98.1, aarch64). On x86_64 
these [build on the `div` 
instruction](https://github.com/rust-lang/rust/blob/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs#L189-L221),
 which divides 128 bits by 64. On other targets they use a [sequence of 64-bit 
hardware 
divides](https://github.com/rust-lang/rust/blob/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs#L142-L158).
 `i256::div_rem` is covered by #11451. #11467 added precomputed divisors for 
32-bit and 64-bit integers. The decimal kernels that divide by a scalar need 
the same for the wider types.
   
   ### Describe the solution you'd like
   
   Add `DivisorU128`, `DivisorI128` and `DivisorI256` (and a `u256` form if the 
kernels need one) to `arrow-buffer`, following the API of #11467: each is a 
`#[non_exhaustive]` enum whose variants are branch-free types, and the variant 
set differs by type. Which variants the wider types get, such as `Up` for 
`u128`, is open.
   
   Tests are the ones from #11467 plus `10^k - 1`, `10^k`, `10^k + 1`, and 
values on each side of 2^64 (and 2^128 for `i256`).
   
   #### Choosing the variant per batch
   
   A kernel would pick the narrowest variant that fits a batch from a scan of 
the values' magnitude, so the API has to let a kernel build the 64-bit divisor 
for an `i128` divisor, or the 128-bit one for an `i256` divisor, when every 
value in the batch fits. One variant per type isn't enough:
   
   - The 128-bit variant alone made 18-digit `Decimal128` casts 16% slower than 
today (24.8 vs 21.3 us per 8192-value batch), while the 64-bit variant made 
them 1.9x faster (11.5 us).
   - In Comet, the 128-bit variant made `i256` products that fit in `i128` 4.2x 
faster than today (63.4 vs 266.5 us).
   - The scan cost 4% to 8% when it found wide values.
   
   The choice can't come from the declared precision, because arrow doesn't 
check stored values against it 
([`with_precision_and_scale`](https://github.com/apache/arrow-rs/blob/6b34163d51aa59f7cac2edcb74e8bd1ddc6b9120/arrow-array/src/array/primitive_array.rs#L1709-L1715)).
 Whether the scan lives in `arrow-buffer` or in each kernel should be settled 
in this PR, because the API is permanent once released.
   
   #### Construction cost
   
   In the prototype, building a divisor costs 22 ns for `i128` and 451 ns for 
`i256`. The `i256` constructor divides one bit at a time and could use 
Algorithm D instead. That's at most 0.45 us per 8192-value batch. For scalars 
and very small batches plain division is cheaper, so kernels need a row-count 
threshold.
   
   ### Describe alternatives you've considered
   
   Precomputed tables of the reciprocals of 10^k would remove the construction 
cost for decimal rescaling. The cost is small enough per batch that I'd start 
without them.
   
   ### Additional context
   
   Comet's `wide_decimal` benchmark evaluates the `WideDecimalBinaryExpr` 
multiply on an 8192-row batch, with each method in the kernel. Today is 
`div_round_half_up`. Times are in us per batch on an Apple M5 Max, medians of 
three runs.
   
   <img width="1600" height="884" alt="Image" 
src="https://github.com/user-attachments/assets/3e26139c-8ea7-4b78-9c99-08dcf5315c15";
 />
   
   | Case | Today | One Algorithm D call, remainder by multiply | 
Granlund-Montgomery, 256-bit | Granlund-Montgomery, 128-bit when products fit |
   |---|---|---|---|---|
   | The existing 
[`multiply`](https://github.com/apache/datafusion-comet/blob/8d0bb01a5a99924024e07c1722e6eddd9260e167/native/spark-expr/benches/wide_decimal.rs#L156)
 case, 30-digit products | 266.5 | 233.8 (1.1x) | 148.4 (1.8x) | **63.4 
(4.2x)** |
   | 48-digit products, 34-digit quotients | 398.1 | 301.9 (1.3x) | **147.1 
(2.7x)** | 153.2 (2.6x) |
   | 42-digit products, 8-digit quotients | 351.5 | 234.5 (1.5x) | **137.1 
(2.6x)** | 143.6 (2.4x) |
   
   The prototype's divisors are in 
[`divproto/`](https://github.com/mbutrovich/arrow-rs/tree/ee0358cd48c9bdeb7ca5a4c52d479f949c17f736/divproto).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to