-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Speedup Uint::shr
#753
Draft
erik-3milabs
wants to merge
21
commits into
RustCrypto:master
Choose a base branch
from
erik-3milabs:speedup_shr
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Speedup Uint::shr
#753
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
509c51c
Make `Uint::as_limbs_mut` const
erik-3milabs 3b78bd5
Implement `Limb::carrying_shr`
erik-3milabs 16f59fc
Implement `Uint::split_overflowing_shr`
erik-3milabs 068b054
Introduce `Uint::fast_split_overflowing_shr`
erik-3milabs dd28ce4
Fix benches
erik-3milabs a5dbc74
Remove integer division/remainder from `split_overflowing_shr`
erik-3milabs 6858e22
Add annotations
erik-3milabs fb15f24
Replace `Uint::shr` with the new implementation.
erik-3milabs 88f1972
Fix `Uint::intra_limb_shr` naming
erik-3milabs d24b299
Fix `Uint::full_limb_shr` naming
erik-3milabs a49f889
Update `Uint::shr` benchmarking
erik-3milabs 271eadb
Implement `Limb::carrying_shl`
erik-3milabs 3d7c87e
Speed up `Uint::shl`
erik-3milabs 6949c33
Remove duplicate shr/shl code
erik-3milabs 81de22d
Remove duplicate `Uint::shl` code
erik-3milabs d438508
Deprecate `Limb::shl1`
erik-3milabs aeb0f73
Remove duplicate `Uint::shr` code
erik-3milabs b138396
Deprecate `Limb::shr1`
erik-3milabs fb01e75
Fix fmt
erik-3milabs ada14bc
Expand `Uint::shl` benchmarking
erik-3milabs 3a1eb7a
Fix fmt
erik-3milabs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,120 @@ impl<const LIMBS: usize> Uint<LIMBS> { | |
ConstCtOption::new(Uint::select(&result, &Self::ZERO, overflow), overflow.not()) | ||
} | ||
|
||
/// Computes `self >> shift`. | ||
/// | ||
/// Returns `None` if `shift >= Self::BITS`. | ||
pub const fn split_overflowing_shr(&self, shift: u32) -> ConstCtOption<Self> { | ||
// Split shift into (shift % Limb::BITS, shift / Limb::BITS) | ||
// Since Limb::BITS is known to be a power of two, this can also be computed as follows: | ||
let limb_bits_bits = u32::BITS - (Limb::BITS - 1).leading_zeros(); | ||
let intra_limb_shift = shift & (Limb::BITS - 1); | ||
let limb_shift = shift >> limb_bits_bits; | ||
self.intra_limb_carrying_shr_internal(intra_limb_shift) | ||
.full_limb_shr(limb_shift) | ||
} | ||
|
||
/// Computes `self >> shift`, for `shift < Limb::BITS`. | ||
/// | ||
/// Returns `None` if `shift >= Limb::BITS`. | ||
pub const fn intra_limb_overflowing_shr(&self, shift: u32) -> ConstCtOption<Self> { | ||
let overflow = ConstChoice::from_u32_lt(shift, Limb::BITS).not(); | ||
let result = self.intra_limb_carrying_shr_internal(shift % Limb::BITS); | ||
ConstCtOption::new(Uint::select(&result, &Self::ZERO, overflow), overflow.not()) | ||
} | ||
|
||
/// Computes `self >> shift`, for `shift < Limb::BITS`. | ||
/// | ||
/// Panics if `shift >= Limb::BITS`. | ||
#[inline(always)] | ||
const fn intra_limb_carrying_shr_internal(&self, shift: u32) -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: not being There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
debug_assert!(shift < Limb::BITS); | ||
|
||
let (mut result, mut carry) = (*self, Limb::ZERO); | ||
|
||
let limbs = result.as_limbs_mut(); | ||
let mut i = limbs.len(); | ||
while i > 0 { | ||
i -= 1; | ||
let (shifted, new_carry) = limbs[i].carrying_shr(shift); | ||
limbs[i] = shifted.bitxor(carry); | ||
carry = new_carry; | ||
} | ||
|
||
result | ||
} | ||
|
||
/// Compute `self >> (Limb::BITS * limb_shift)`, for `limb_shift < Self::LIMBS`. | ||
/// | ||
/// Returns `None` if `limb_shift >= Self::LIMBS`. | ||
#[inline(always)] | ||
pub const fn full_limb_shr(&self, limb_shift: u32) -> ConstCtOption<Self> { | ||
let shift_bits = u32::BITS - (LIMBS as u32 - 1).leading_zeros(); | ||
let overflow = ConstChoice::from_u32_lt(limb_shift, LIMBS as u32).not(); | ||
let limb_shift = limb_shift % LIMBS as u32; | ||
|
||
let mut result = *self; | ||
let mut i = 0; | ||
while i < shift_bits { | ||
let bit = ConstChoice::from_u32_lsb((limb_shift >> i) & 1); | ||
result = Uint::select( | ||
&result, | ||
&result | ||
.overflowing_shr_vartime(Limb::BITS << i) | ||
.expect("shift within range"), | ||
bit, | ||
); | ||
i += 1; | ||
} | ||
|
||
ConstCtOption::new(Uint::select(&result, &Self::ZERO, overflow), overflow.not()) | ||
} | ||
|
||
/// Computes `self >> shift`. | ||
/// | ||
/// Returns `None` if `shift >= Self::BITS`. | ||
pub const fn fast_split_overflowing_shr(&self, shift: u32) -> ConstCtOption<Self> { | ||
// Split shift into (shift % Limb::BITS, shift / Limb::BITS) | ||
// Since Limb::BITS is known to be a power of two, this can also be computed as follows: | ||
let limb_bits_bits = u32::BITS - (Limb::BITS - 1).leading_zeros(); | ||
let intra_limb_shift = shift & (Limb::BITS - 1); | ||
let limb_shift = shift >> limb_bits_bits; | ||
self.intra_limb_carrying_shr_internal(intra_limb_shift) | ||
.fast_full_limb_shr(limb_shift) | ||
} | ||
|
||
/// Compute `self >> (Limb::BITS * limb_shift)`, for `limb_shift < Self::LIMBS`. | ||
/// | ||
/// Returns `None` if `limb_shift >= Self::LIMBS`. | ||
#[inline(always)] | ||
pub const fn fast_full_limb_shr(&self, limb_shift: u32) -> ConstCtOption<Self> { | ||
let shift_bits = u32::BITS - (LIMBS as u32 - 1).leading_zeros(); | ||
let overflow = ConstChoice::from_u32_lt(limb_shift, LIMBS as u32).not(); | ||
let limb_shift = limb_shift % LIMBS as u32; | ||
|
||
let mut result = *self; | ||
let mut i = 0; | ||
while i < shift_bits { | ||
let bit = ConstChoice::from_u32_lsb((limb_shift >> i) & 1); | ||
|
||
let mut j = 0; | ||
let limbs = result.as_limbs_mut(); | ||
let offset = 1 << i; | ||
while j < Self::LIMBS.saturating_sub(offset) { | ||
limbs[j] = Limb::select(limbs[j], limbs[j + offset], bit); | ||
j += 1; | ||
} | ||
while j < Self::LIMBS { | ||
limbs[j] = Limb::select(limbs[j], Limb::ZERO, bit); | ||
j += 1; | ||
} | ||
|
||
i += 1; | ||
} | ||
|
||
ConstCtOption::new(Uint::select(&result, &Self::ZERO, overflow), overflow.not()) | ||
} | ||
|
||
/// Computes `self >> shift`. | ||
/// | ||
/// Returns `None` if `shift >= Self::BITS`. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use crypto_bigint::{Limb, Word}; | ||
use proptest::prelude::*; | ||
|
||
prop_compose! { | ||
fn limb()(x in any::<Word>()) -> Limb { | ||
Limb::from(x) | ||
} | ||
} | ||
proptest! { | ||
#[test] | ||
fn carrying_shr_doesnt_panic(limb in limb(), shift in 0..32u32) { | ||
limb.carrying_shr(shift); | ||
} | ||
|
||
#[test] | ||
fn carrying_shr(limb in limb(), shift in 0..32u32) { | ||
if shift == 0 { | ||
assert_eq!(limb.carrying_shr(shift), (limb, Limb::ZERO)); | ||
} else { | ||
assert_eq!(limb.carrying_shr(shift), (limb.shr(shift), limb.shl(Limb::BITS - shift))); | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!