Skip to content

Commit edf1f46

Browse files
authored
Bernstein-Yang: boxing optimizations (#653)
Adds a `BoxedUnsatInt::from_uint_widened` constructor which can be used to pad all `BoxedUnsatInt`s used as parameters to Bernstein-Yang to the same size at allocation time, rather than retroactively widening them to be the same size which may incur an additional allocation.
1 parent 6df8d89 commit edf1f46

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed

src/modular/bernstein_yang/boxed.rs

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,26 @@ impl Inverter for BoxedBernsteinYangInverter {
6969
}
7070
}
7171

72+
/// Compute the number of unsaturated limbs needed to represent a saturated integer with the given
73+
/// number of saturated limbs.
74+
fn unsat_nlimbs_for_sat_nlimbs(saturated_nlimbs: usize) -> usize {
75+
let saturated_nlimbs = if Word::BITS == 32 && saturated_nlimbs == 1 {
76+
2
77+
} else {
78+
saturated_nlimbs
79+
};
80+
81+
bernstein_yang_nlimbs!(saturated_nlimbs * Limb::BITS as usize)
82+
}
83+
7284
/// Returns the greatest common divisor (GCD) of the two given numbers.
7385
pub(crate) fn gcd(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
74-
let nlimbs = bernstein_yang_nlimbs!(max(f.nlimbs(), g.nlimbs()) * Limb::BITS as usize);
75-
86+
let nlimbs = unsat_nlimbs_for_sat_nlimbs(max(f.nlimbs(), g.nlimbs()));
7687
let bits_precision = f.bits_precision();
88+
7789
let inverse = inv_mod2_62(f.as_words());
78-
let f = BoxedUnsatInt::from(f).widen(nlimbs);
79-
let mut g = BoxedUnsatInt::from(g).widen(nlimbs);
90+
let f = BoxedUnsatInt::from_uint_widened(f, nlimbs);
91+
let mut g = BoxedUnsatInt::from_uint_widened(g, nlimbs);
8092
let mut d = BoxedUnsatInt::zero(nlimbs);
8193
let e = BoxedUnsatInt::one(nlimbs);
8294

@@ -89,12 +101,12 @@ pub(crate) fn gcd(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
89101
///
90102
/// Variable time with respect to `g`.
91103
pub(crate) fn gcd_vartime(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
92-
let nlimbs = bernstein_yang_nlimbs!(max(f.nlimbs(), g.nlimbs()) * Limb::BITS as usize);
93-
104+
let nlimbs = unsat_nlimbs_for_sat_nlimbs(max(f.nlimbs(), g.nlimbs()));
94105
let bits_precision = f.bits_precision();
106+
95107
let inverse = inv_mod2_62(f.as_words());
96-
let f = BoxedUnsatInt::from(f).widen(nlimbs);
97-
let mut g = BoxedUnsatInt::from(g).widen(nlimbs);
108+
let f = BoxedUnsatInt::from_uint_widened(f, nlimbs);
109+
let mut g = BoxedUnsatInt::from_uint_widened(g, nlimbs);
98110
let mut d = BoxedUnsatInt::zero(nlimbs);
99111
let e = BoxedUnsatInt::one(nlimbs);
100112

@@ -238,15 +250,31 @@ pub(crate) struct BoxedUnsatInt(Box<[u64]>);
238250
///
239251
/// The ordering of the chunks in these arrays is little-endian.
240252
impl From<&BoxedUint> for BoxedUnsatInt {
241-
#[allow(trivial_numeric_casts)]
242253
fn from(input: &BoxedUint) -> BoxedUnsatInt {
243-
let saturated_nlimbs = if Word::BITS == 32 && input.nlimbs() == 1 {
244-
2
245-
} else {
246-
input.nlimbs()
247-
};
254+
Self::from_uint_widened(input, unsat_nlimbs_for_sat_nlimbs(input.nlimbs()))
255+
}
256+
}
257+
258+
impl BoxedUnsatInt {
259+
/// Number of bits in each limb.
260+
pub const LIMB_BITS: usize = 62;
248261

249-
let nlimbs = bernstein_yang_nlimbs!(saturated_nlimbs * Limb::BITS as usize);
262+
/// Mask, in which the 62 lowest bits are 1.
263+
pub const MASK: u64 = u64::MAX >> (64 - Self::LIMB_BITS);
264+
265+
/// Convert from 32/64-bit saturated representation used by `BoxedUint` to the 62-bit
266+
/// unsaturated representation used by `BoxedUnsatInt`.
267+
///
268+
/// Returns a big unsigned integer as an array of 62-bit chunks, which is equal modulo
269+
/// 2 ^ (62 * S) to the input big unsigned integer stored as an array of 64-bit chunks.
270+
///
271+
/// The ordering of the chunks in these arrays is little-endian.
272+
///
273+
/// The `nlimbs` parameter defines the number of unsaturated limbs in the output.
274+
/// It's provided explicitly so multiple values can be padded to the same size.
275+
#[allow(trivial_numeric_casts)]
276+
fn from_uint_widened(input: &BoxedUint, nlimbs: usize) -> BoxedUnsatInt {
277+
debug_assert!(nlimbs >= unsat_nlimbs_for_sat_nlimbs(input.nlimbs()));
250278

251279
// Workaround for 32-bit platforms: if the input is a single limb, it will be smaller input
252280
// than is usable for Bernstein-Yang with is currently natively 64-bits on all targets
@@ -263,14 +291,6 @@ impl From<&BoxedUint> for BoxedUnsatInt {
263291
impl_limb_convert!(Word, Word::BITS as usize, input, u64, 62, output);
264292
Self(output.into())
265293
}
266-
}
267-
268-
impl BoxedUnsatInt {
269-
/// Number of bits in each limb.
270-
pub const LIMB_BITS: usize = 62;
271-
272-
/// Mask, in which the 62 lowest bits are 1.
273-
pub const MASK: u64 = u64::MAX >> (64 - Self::LIMB_BITS);
274294

275295
/// Convert to a `BoxedUint` of the given precision.
276296
#[allow(trivial_numeric_casts)]

src/modular/bernstein_yang/macros.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
/// Write an impl of a limb conversion function.
44
///
5-
/// Workaround for making this function generic around limb types while still allowing it to be `const fn`.
5+
/// Workaround for making this function generic around limb types while still allowing it to be
6+
/// `const fn`.
67
macro_rules! impl_limb_convert {
78
($input_type:ty, $input_bits:expr, $input:expr, $output_type:ty, $output_bits:expr, $output:expr) => {{
89
// This function is defined because the method "min" of the usize type is not constant

0 commit comments

Comments
 (0)