From 8df733e007d7c4e24c8dfa7e373ba4889426502f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 11 Apr 2024 19:29:19 +0200 Subject: [PATCH 1/3] Optimize isqrt max bound --- packages/core/src/math/isqrt.rs | 33 +++++++++++++++++++++++++++++-- packages/core/src/math/uint128.rs | 10 ++++++++++ packages/core/src/math/uint256.rs | 10 ++++++++++ packages/core/src/math/uint512.rs | 10 ++++++++++ packages/core/src/math/uint64.rs | 10 ++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/packages/core/src/math/isqrt.rs b/packages/core/src/math/isqrt.rs index 837cada5ae..1a67ef16f8 100644 --- a/packages/core/src/math/isqrt.rs +++ b/packages/core/src/math/isqrt.rs @@ -13,8 +13,10 @@ pub trait Isqrt { impl Isqrt for I where I: Unsigned + + Log2 + ops::Add + ops::Div + + ops::Shl + ops::Shr + cmp::PartialOrd + Copy @@ -23,9 +25,13 @@ where /// Algorithm adapted from /// [Wikipedia](https://en.wikipedia.org/wiki/Integer_square_root#Example_implementation_in_C). fn isqrt(self) -> Self { - let mut x0 = self >> 1; + let zero = Self::from(0); + if self == zero { + return zero; + } + let mut x0 = Self::from(1u8) << ((self.log_2() / 2) + 1); - if x0 > 0.into() { + if x0 > zero { let mut x1 = (x0 + self / x0) >> 1; while x1 < x0 { @@ -52,6 +58,29 @@ impl Unsigned for Uint256 {} impl Unsigned for Uint512 {} impl Unsigned for usize {} +trait Log2 { + fn log_2(self) -> u32; +} +macro_rules! impl_log2 { + ($type:ty) => { + impl Log2 for $type { + fn log_2(self) -> u32 { + self.ilog2() + } + } + }; +} +impl_log2!(u8); +impl_log2!(u16); +impl_log2!(u32); +impl_log2!(u64); +impl_log2!(u128); +impl_log2!(usize); +impl_log2!(Uint64); +impl_log2!(Uint128); +impl_log2!(Uint256); +impl_log2!(Uint512); + #[cfg(test)] mod tests { use super::*; diff --git a/packages/core/src/math/uint128.rs b/packages/core/src/math/uint128.rs index b95c5bafe8..e97621f926 100644 --- a/packages/core/src/math/uint128.rs +++ b/packages/core/src/math/uint128.rs @@ -96,6 +96,16 @@ impl Uint128 { self.0.pow(exp).into() } + /// Returns the base 2 logarithm of the number, rounded down. + /// + /// # Panics + /// + /// This function will panic if `self` is zero. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ilog2(self) -> u32 { + self.0.checked_ilog2().unwrap() + } + /// Returns `self * numerator / denominator`. /// /// Due to the nature of the integer division involved, the result is always floored. diff --git a/packages/core/src/math/uint256.rs b/packages/core/src/math/uint256.rs index 92fbaf2928..fa7f3a69b1 100644 --- a/packages/core/src/math/uint256.rs +++ b/packages/core/src/math/uint256.rs @@ -168,6 +168,16 @@ impl Uint256 { Self(self.0.pow(exp)) } + /// Returns the base 2 logarithm of the number, rounded down. + /// + /// # Panics + /// + /// This function will panic if `self` is zero. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ilog2(self) -> u32 { + self.0.checked_ilog2().unwrap() + } + /// Returns `self * numerator / denominator`. /// /// Due to the nature of the integer division involved, the result is always floored. diff --git a/packages/core/src/math/uint512.rs b/packages/core/src/math/uint512.rs index e4640d7b2e..81738bbac9 100644 --- a/packages/core/src/math/uint512.rs +++ b/packages/core/src/math/uint512.rs @@ -193,6 +193,16 @@ impl Uint512 { Self(self.0.pow(exp)) } + /// Returns the base 2 logarithm of the number, rounded down. + /// + /// # Panics + /// + /// This function will panic if `self` is zero. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ilog2(self) -> u32 { + self.0.checked_ilog2().unwrap() + } + pub fn checked_add(self, other: Self) -> Result { self.0 .checked_add(other.0) diff --git a/packages/core/src/math/uint64.rs b/packages/core/src/math/uint64.rs index ea684229c5..b54aed1e04 100644 --- a/packages/core/src/math/uint64.rs +++ b/packages/core/src/math/uint64.rs @@ -90,6 +90,16 @@ impl Uint64 { self.0.pow(exp).into() } + /// Returns the base 2 logarithm of the number, rounded down. + /// + /// # Panics + /// + /// This function will panic if `self` is zero. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ilog2(self) -> u32 { + self.0.checked_ilog2().unwrap() + } + /// Returns `self * numerator / denominator`. /// /// Due to the nature of the integer division involved, the result is always floored. From c63227890d9274caf61d4d132396996cf0d375d0 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 11 Apr 2024 19:45:56 +0200 Subject: [PATCH 2/3] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 283d3322d9..57f294a529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ and this project adheres to - cosmwasm-schema-derive: Improve emitted error messages ([#2063]) - cosmwasm-schema: `#[cw_serde]` now doesn't add `#[serde(deny_unknown_fields)]` to the expanded code anymore ([#2080]) +- cosmwasm-std: Improve performance of `Uint{64,128,256,512}::isqrt` ([#2108]) [#2044]: https://github.com/CosmWasm/cosmwasm/pull/2044 [#2051]: https://github.com/CosmWasm/cosmwasm/pull/2051 @@ -54,6 +55,7 @@ and this project adheres to [#2063]: https://github.com/CosmWasm/cosmwasm/pull/2063 [#2070]: https://github.com/CosmWasm/cosmwasm/pull/2070 [#2080]: https://github.com/CosmWasm/cosmwasm/pull/2080 +[#2108]: https://github.com/CosmWasm/cosmwasm/pull/2108 ## [2.0.1] - 2024-04-03 From 8ddbb68508ab8c2588da795100a83b4d63ba3e0d Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 12 Apr 2024 10:28:18 +0200 Subject: [PATCH 3/3] Improve isqrt implementation further --- packages/core/src/math/isqrt.rs | 62 +++++++++++++++------------------ 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/packages/core/src/math/isqrt.rs b/packages/core/src/math/isqrt.rs index 1a67ef16f8..64d730f12f 100644 --- a/packages/core/src/math/isqrt.rs +++ b/packages/core/src/math/isqrt.rs @@ -13,25 +13,24 @@ pub trait Isqrt { impl Isqrt for I where I: Unsigned - + Log2 + ops::Add + ops::Div + ops::Shl + ops::Shr + cmp::PartialOrd - + Copy - + From, + + Copy, { /// Algorithm adapted from /// [Wikipedia](https://en.wikipedia.org/wiki/Integer_square_root#Example_implementation_in_C). fn isqrt(self) -> Self { - let zero = Self::from(0); - if self == zero { - return zero; + // sqrt(0) = 0, sqrt(1) = 1 + if self <= Self::ONE { + return self; } - let mut x0 = Self::from(1u8) << ((self.log_2() / 2) + 1); - if x0 > zero { + let mut x0 = Self::ONE << ((self.log_2() / 2) + 1); + + if x0 > Self::ZERO { let mut x1 = (x0 + self / x0) >> 1; while x1 < x0 { @@ -46,40 +45,35 @@ where } /// Marker trait for types that represent unsigned integers. -pub trait Unsigned {} -impl Unsigned for u8 {} -impl Unsigned for u16 {} -impl Unsigned for u32 {} -impl Unsigned for u64 {} -impl Unsigned for u128 {} -impl Unsigned for Uint64 {} -impl Unsigned for Uint128 {} -impl Unsigned for Uint256 {} -impl Unsigned for Uint512 {} -impl Unsigned for usize {} - -trait Log2 { +pub trait Unsigned { + const ZERO: Self; + const ONE: Self; + fn log_2(self) -> u32; } -macro_rules! impl_log2 { - ($type:ty) => { - impl Log2 for $type { + +macro_rules! impl_unsigned { + ($type:ty, $zero:expr, $one:expr) => { + impl Unsigned for $type { + const ZERO: Self = $zero; + const ONE: Self = $one; + fn log_2(self) -> u32 { self.ilog2() } } }; } -impl_log2!(u8); -impl_log2!(u16); -impl_log2!(u32); -impl_log2!(u64); -impl_log2!(u128); -impl_log2!(usize); -impl_log2!(Uint64); -impl_log2!(Uint128); -impl_log2!(Uint256); -impl_log2!(Uint512); +impl_unsigned!(u8, 0, 1); +impl_unsigned!(u16, 0, 1); +impl_unsigned!(u32, 0, 1); +impl_unsigned!(u64, 0, 1); +impl_unsigned!(u128, 0, 1); +impl_unsigned!(usize, 0, 1); +impl_unsigned!(Uint64, Self::zero(), Self::one()); +impl_unsigned!(Uint128, Self::zero(), Self::one()); +impl_unsigned!(Uint256, Self::zero(), Self::one()); +impl_unsigned!(Uint512, Self::zero(), Self::one()); #[cfg(test)] mod tests {