Optimize min/max implementation using native PartialOrd#9
Conversation
- Added NumericComparable trait to enable zero-allocation comparisons - Implemented trait for all integer types using BigDecimal's native PartialOrd - Updated max() and min() methods to use NumericComparable - All existing tests pass Co-authored-by: maoueh <123014+maoueh@users.noreply.github.com>
- Added tests for i32, i64, u32 max/min operations - Added tests for BigDecimal and BigInt max/min - Added tests for string max/min to ensure backwards compatibility - Added tests for mixed integer types - All 82 tests pass successfully Co-authored-by: maoueh <123014+maoueh@users.noreply.github.com>
- Added explicit test to verify integer comparison ordering - Confirms that 50 < 100, 200 > 100, and 100 == 100 work correctly - All 83 tests pass successfully Co-authored-by: maoueh <123014+maoueh@users.noreply.github.com>
| // i128 and u128 - use BigDecimal's native PartialOrd for these types | ||
| impl NumericComparable for i128 { | ||
| fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering { | ||
| self.partial_cmp(other) | ||
| .expect("BigDecimal comparison should always succeed for i128") | ||
| } | ||
| } | ||
|
|
||
| impl NumericComparable for u128 { | ||
| fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering { | ||
| self.partial_cmp(other) | ||
| .expect("BigDecimal comparison should always succeed for u128") | ||
| } | ||
| } |
There was a problem hiding this comment.
Seems to me those can be integrated directly into impl_numeric_comparable_for_integer! macro call, implementation is that same.
There was a problem hiding this comment.
Done in commit a1724ba. Integrated i128 and u128 into the macro call since they use the same implementation pattern with native PartialOrd.
| // Direct comparison using BigDecimal's PartialOrd<$t> | ||
| // This is zero-allocation for integers | ||
| self.partial_cmp(other) | ||
| .expect("BigDecimal comparison should always succeed for integers") |
There was a problem hiding this comment.
Replace for integers with for <type> (with correct Rust macro stringify syntax)
There was a problem hiding this comment.
Done in commit a1724ba. Updated to use stringify!($t) in the panic message for accurate type names.
| // String implementations - must parse to BigDecimal | ||
| impl NumericComparable for String { | ||
| fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering { | ||
| let self_bd = BigDecimal::from_str(self).unwrap_or_else(|_| { | ||
| panic!("min/max() requires a valid numeric value, got: {}", self) | ||
| }); | ||
| self_bd.cmp(other) | ||
| } | ||
| } | ||
|
|
||
| impl NumericComparable for &str { | ||
| fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering { | ||
| let self_bd = BigDecimal::from_str(self).unwrap_or_else(|_| { | ||
| panic!("min/max() requires a valid numeric value, got: {}", self) | ||
| }); | ||
| self_bd.cmp(other) | ||
| } | ||
| } |
There was a problem hiding this comment.
Since those panics, I think we shouldn't offer them, user/caller should perform the conversation hisself and use his required types.
There was a problem hiding this comment.
Done in commit a1724ba. Removed String and &str implementations. Users must now convert strings to numeric types themselves before calling min/max.
| /// | ||
| /// This trait leverages BigDecimal's native PartialOrd implementations for primitive | ||
| /// types, enabling zero-allocation comparisons for integers. | ||
| pub trait NumericComparable { |
There was a problem hiding this comment.
Make : ToBigDecimal like NumericAddable.
There was a problem hiding this comment.
Done in commit a1724ba. Made NumericComparable extend ToBigDecimal trait, consistent with NumericAddable pattern.
| /// Used with upsert_row() for tracking high values. | ||
| /// Can only follow set() or another max() call on the same field. | ||
| pub fn max<T: ToDatabaseValue>(&mut self, name: &str, value: T) -> &mut Self { | ||
| pub fn max<T: NumericComparable + ToDatabaseValue>(&mut self, name: &str, value: T) -> &mut Self { |
There was a problem hiding this comment.
We will not accept string, use signature more similar to add/sub, adjust the tests as a consequence of the constraining of accepted types that are now reduced.
There was a problem hiding this comment.
Done in commit a1724ba. Changed signature to <T: NumericComparable> (removed ToDatabaseValue). Updated all tests to use numeric types instead of strings.
| /// Used with upsert_row() for tracking low values. | ||
| /// Can only follow set() or another min() call on the same field. | ||
| pub fn min<T: ToDatabaseValue>(&mut self, name: &str, value: T) -> &mut Self { | ||
| pub fn min<T: NumericComparable + ToDatabaseValue>(&mut self, name: &str, value: T) -> &mut Self { |
There was a problem hiding this comment.
Same comment as max here, adjust accordingly.
There was a problem hiding this comment.
Done in commit a1724ba. Changed min() signature to match max() - now <T: NumericComparable> only. Updated all related tests.
- Made NumericComparable extend ToBigDecimal trait - Integrated i128/u128 into macro (use native PartialOrd) - Added ToBigDecimal/NumericAddable for isize/usize/i128/u128 - Improved panic message to use stringify!($t) for type name - Removed String/&str NumericComparable implementations - Changed max/min signatures to match add/sub pattern (removed ToDatabaseValue) - Updated all tests to use integers instead of strings - Removed string-specific tests Co-authored-by: maoueh <123014+maoueh@users.noreply.github.com>
BLO-806: Optimize min/max Implementation ✅
Successfully implemented the
NumericComparabletrait to optimize min/max operations by leveraging BigDecimal's native PartialOrd implementations.Implementation Checklist
NumericComparabletrait tosrc/numeric/traits.rsToBigDecimaltrait for consistency withNumericAddablesrc/numeric/impls.rsToBigDecimalandNumericAddablefor isize/usize/i128/u128src/numeric/mod.rsto exportNumericComparablesrc/tables.rsNumericComparableandOrderingmax()method to useNumericComparabletrait only (noToDatabaseValue)min()method to useNumericComparabletrait only (noToDatabaseValue)Key Changes
1. New Trait:
NumericComparableToBigDecimaltrait (consistent withNumericAddablepattern)PartialOrdimplementations for zero-allocation comparisons2. Optimized Methods
max()andmin()signatures now matchadd()/sub()pattern:<T: NumericComparable>cmp_to_big_decimal()for integersto_big_decimal().to_string()only when storing values3. Comprehensive Testing
Performance Impact
Before:
After (for integers):
Results:
add()/sub()methodsstringify!API Changes
Breaking Change: The
max()andmin()methods no longer accept string types directly. Users must convert strings to appropriate numeric types before calling these methods.Before:
After:
Verification
Files Modified
src/numeric/traits.rs: NumericComparable extends ToBigDecimalsrc/numeric/impls.rs: Refined implementations, added missing typessrc/numeric/mod.rs: Export NumericComparablesrc/tables.rs: Updated signatures and all testsFixes: https://linear.app/streamingfast/issue/BLO-806/optimize-minmax-implementation
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.