mbutrovich commented on code in PR #4926:
URL: https://github.com/apache/datafusion-comet/pull/4926#discussion_r3591143087


##########
native/spark-expr/src/math_funcs/utils.rs:
##########


Review Comment:
   `utils.rs:49` still declares `f: &dyn Fn(i128) -> i128` and `ceil.rs:68` 
passes `&f`. The PR's stated rationale is "the per-element rounding closure is 
monomorphized rather than called through a `&dyn Fn` vtable," but that only 
happened for the array path. The scalar path is a single element so the perf 
cost is irrelevant, however the two helpers now have inconsistent signatures 
for no reason, which is a maintenance trap.
   
   Suggested change: change `make_decimal_scalar` at `utils.rs:49` to `f: impl 
Fn(i128) -> i128` and pass `f` by value at `ceil.rs:68`, matching 
`make_decimal_array`. This keeps the two helpers symmetric.



##########
native/spark-expr/src/math_funcs/ceil.rs:
##########
@@ -78,7 +78,20 @@ pub fn spark_ceil(
 #[inline]
 fn decimal_ceil_f(scale: &i8) -> impl Fn(i128) -> i128 {
     let div = 10_i128.pow_wrapping(*scale as u32);
-    move |x: i128| div_ceil(x, div)
+    // 128-bit division is a compiler intrinsic call, while 64-bit division 
maps to a single
+    // hardware instruction. Decimals with precision <= 18 always fit in 64 
bits, so take that
+    // path whenever both the value and the divisor do, and fall back to 
128-bit otherwise.
+    let div_i64 = i64::try_from(div).ok();
+    move |x: i128| match (div_i64, i64::try_from(x)) {
+        (Some(d), Ok(x)) => {
+            let quotient = x / d;
+            // `d` is positive, so a positive remainder means the truncated 
quotient is below
+            // the true value and has to be rounded up.
+            let adjusted = if x % d > 0 { quotient + 1 } else { quotient };
+            adjusted as i128
+        }
+        _ => div_ceil(x, div),
+    }
 }
 

Review Comment:
   `ceil.rs:97-254` tests only small unscaled values (max `12999`), which all 
take the new i64 fast path. The entire point of this PR is the branch at 
`ceil.rs:85`, and the fallback arm (`_ => div_ceil(x, div)` at `ceil.rs:93`) 
has zero direct coverage. A regression that broke only the wide path would pass 
CI.
   
   Suggested change: add an array test with a `Decimal128(38, s)` input whose 
unscaled values exceed `i64::MAX` (positive and negative), asserting the ceil 
result. Example: unscaled `20_000_000_000_000_000_000` (> i64::MAX) at scale 6 
targeting `Decimal128(38, 0)`, plus a negative sibling and an exact multiple, 
so both the fast and fallback arms plus the negative-remainder branch are 
covered in one suite.



##########
native/spark-expr/src/math_funcs/utils.rs:
##########
@@ -57,7 +57,7 @@ pub(crate) fn make_decimal_array(
     array: &ArrayRef,
     precision: u8,
     scale: i8,
-    f: &dyn Fn(i128) -> i128,
+    f: impl Fn(i128) -> i128,

Review Comment:
   `utils.rs:60` now takes `f: impl Fn(i128) -> i128`, but the array call site 
at `ceil.rs:51` still passes `&f`. This compiles because `&F: Fn` via the 
blanket impl, so `F` monomorphizes to `&closure` and dispatch is static, but it 
is now a stray reference that obscures the intent of the change and adds a 
pointer indirection the PR was trying to remove.
   
   Suggested change: pass the closure by value at `ceil.rs:51`: 
`make_decimal_array(array, precision, scale, f)`.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to