Skip to content

Commit

Permalink
Montgomery form arithmetic improvements (#664)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Whitehead <[email protected]>
  • Loading branch information
andrewwhitehead authored Sep 19, 2024
1 parent 6cb2d09 commit 20059a7
Show file tree
Hide file tree
Showing 17 changed files with 429 additions and 44 deletions.
68 changes: 65 additions & 3 deletions benches/boxed_monty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,69 @@ fn to_biguint(uint: &BoxedUint) -> BigUint {
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
let params = BoxedMontyParams::new(Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS));

group.bench_function("invert, 4096-bit", |b| {
group.bench_function(format!("add, {UINT_BITS}-bit"), |b| {
b.iter_batched(
|| {
let a = BoxedMontyForm::new(
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
);
let b = BoxedMontyForm::new(
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
);
(a, b)
},
|(a, b)| black_box(a).add(&black_box(b)),
BatchSize::SmallInput,
)
});

group.bench_function(format!("double, {UINT_BITS}-bit"), |b| {
b.iter_batched(
|| {
BoxedMontyForm::new(
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
)
},
|a| black_box(a).double(),
BatchSize::SmallInput,
)
});

group.bench_function(format!("sub, {UINT_BITS}-bit"), |b| {
b.iter_batched(
|| {
let a = BoxedMontyForm::new(
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
);
let b = BoxedMontyForm::new(
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
);
(a, b)
},
|(a, b)| black_box(a).sub(&black_box(b)),
BatchSize::SmallInput,
)
});

group.bench_function(format!("neg, {UINT_BITS}-bit"), |b| {
b.iter_batched(
|| {
BoxedMontyForm::new(
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
)
},
|a| black_box(a).neg(),
BatchSize::SmallInput,
)
});

group.bench_function(format!("invert, {UINT_BITS}-bit"), |b| {
b.iter_batched(
|| {
BoxedMontyForm::new(
Expand All @@ -36,11 +98,11 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
b.iter_batched(
|| {
let x = BoxedMontyForm::new(
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
);
let y = BoxedMontyForm::new(
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params.clone(),
);
(x, y)
Expand Down
72 changes: 60 additions & 12 deletions benches/const_monty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use criterion::{
black_box, criterion_group, criterion_main, measurement::Measurement, BatchSize,
BenchmarkGroup, Criterion,
};
use crypto_bigint::{impl_modulus, modular::ConstMontyParams, Invert, Inverter, Random, U256};
use crypto_bigint::{
impl_modulus, modular::ConstMontyParams, Invert, Inverter, Random, RandomMod, U256,
};
use rand_core::OsRng;

#[cfg(feature = "alloc")]
Expand All @@ -19,25 +21,65 @@ type ConstMontyForm = crypto_bigint::modular::ConstMontyForm<Modulus, { U256::LI
fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("ConstMontyForm creation", |b| {
b.iter_batched(
|| U256::random(&mut OsRng),
|| U256::random_mod(&mut OsRng, Modulus::MODULUS.as_nz_ref()),
|x| black_box(ConstMontyForm::new(&x)),
BatchSize::SmallInput,
)
});

group.bench_function("ConstMontyForm retrieve", |b| {
b.iter_batched(
|| ConstMontyForm::new(&U256::random(&mut OsRng)),
|| ConstMontyForm::random(&mut OsRng),
|x| black_box(x.retrieve()),
BatchSize::SmallInput,
)
});
}

fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("add, U256", |b| {
b.iter_batched(
|| {
let a = ConstMontyForm::random(&mut OsRng);
let b = ConstMontyForm::random(&mut OsRng);
(a, b)
},
|(a, b)| black_box(a).add(&black_box(b)),
BatchSize::SmallInput,
)
});

group.bench_function("double, U256", |b| {
b.iter_batched(
|| ConstMontyForm::random(&mut OsRng),
|a| black_box(a).double(),
BatchSize::SmallInput,
)
});

group.bench_function("sub, U256", |b| {
b.iter_batched(
|| {
let a = ConstMontyForm::random(&mut OsRng);
let b = ConstMontyForm::random(&mut OsRng);
(a, b)
},
|(a, b)| black_box(a).sub(&black_box(b)),
BatchSize::SmallInput,
)
});

group.bench_function("neg, U256", |b| {
b.iter_batched(
|| ConstMontyForm::random(&mut OsRng),
|a| black_box(a).neg(),
BatchSize::SmallInput,
)
});

group.bench_function("invert, U256", |b| {
b.iter_batched(
|| ConstMontyForm::new(&U256::random(&mut OsRng)),
|| ConstMontyForm::random(&mut OsRng),
|x| black_box(x).invert(),
BatchSize::SmallInput,
)
Expand All @@ -46,7 +88,7 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("Bernstein-Yang invert, U256", |b| {
b.iter_batched(
|| {
let x = ConstMontyForm::new(&U256::random(&mut OsRng));
let x = ConstMontyForm::random(&mut OsRng);
let inverter = Modulus::precompute_inverter();
(x, inverter)
},
Expand All @@ -58,20 +100,27 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("multiplication, U256*U256", |b| {
b.iter_batched(
|| {
let x = ConstMontyForm::new(&U256::random(&mut OsRng));
let y = ConstMontyForm::new(&U256::random(&mut OsRng));
let x = ConstMontyForm::random(&mut OsRng);
let y = ConstMontyForm::random(&mut OsRng);
(x, y)
},
|(x, y)| black_box(x * y),
|(x, y)| black_box(x).mul(&black_box(y)),
BatchSize::SmallInput,
)
});

group.bench_function("squaring, U256*U256", |b| {
b.iter_batched(
|| ConstMontyForm::random(&mut OsRng),
|x| black_box(x).square(),
BatchSize::SmallInput,
)
});

group.bench_function("modpow, U256^U256", |b| {
b.iter_batched(
|| {
let x = U256::random(&mut OsRng);
let x_m = ConstMontyForm::new(&x);
let x_m = ConstMontyForm::random(&mut OsRng);
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
(x_m, p)
},
Expand All @@ -89,8 +138,7 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
|| {
let bases_and_exponents: Vec<(ConstMontyForm, U256)> = (1..=i)
.map(|_| {
let x = U256::random(&mut OsRng);
let x_m = ConstMontyForm::new(&x);
let x_m = ConstMontyForm::random(&mut OsRng);
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
(x_m, p)
})
Expand Down
105 changes: 94 additions & 11 deletions benches/monty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use criterion::{
};
use crypto_bigint::{
modular::{MontyForm, MontyParams},
Invert, Inverter, Odd, PrecomputeInverter, Random, U256,
Invert, Inverter, Odd, PrecomputeInverter, Random, RandomMod, U256,
};
use rand_core::OsRng;

Expand All @@ -31,7 +31,7 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
let params = MontyParams::new_vartime(Odd::<U256>::random(&mut OsRng));
group.bench_function("MontyForm::new", |b| {
b.iter_batched(
|| Odd::<U256>::random(&mut OsRng),
|| U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
|x| black_box(MontyForm::new(&x, params)),
BatchSize::SmallInput,
)
Expand All @@ -40,7 +40,12 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
let params = MontyParams::new_vartime(Odd::<U256>::random(&mut OsRng));
group.bench_function("MontyForm retrieve", |b| {
b.iter_batched(
|| MontyForm::new(&U256::random(&mut OsRng), params),
|| {
MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
)
},
|x| black_box(x.retrieve()),
BatchSize::SmallInput,
)
Expand All @@ -50,9 +55,76 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
let params = MontyParams::new_vartime(Odd::<U256>::random(&mut OsRng));

group.bench_function("add, U256", |b| {
b.iter_batched(
|| {
let a = MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
);
let b = MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
);
(a, b)
},
|(a, b)| black_box(a).add(&black_box(b)),
BatchSize::SmallInput,
)
});

group.bench_function("double, U256", |b| {
b.iter_batched(
|| {
MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
)
},
|a| black_box(a).double(),
BatchSize::SmallInput,
)
});

group.bench_function("sub, U256", |b| {
b.iter_batched(
|| {
let a = MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
);
let b = MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
);
(a, b)
},
|(a, b)| black_box(a).sub(&black_box(b)),
BatchSize::SmallInput,
)
});

group.bench_function("neg, U256", |b| {
b.iter_batched(
|| {
MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
)
},
|a| black_box(a).neg(),
BatchSize::SmallInput,
)
});

group.bench_function("invert, U256", |b| {
b.iter_batched(
|| MontyForm::new(&U256::random(&mut OsRng), params),
|| {
MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
)
},
|x| black_box(x).invert(),
BatchSize::SmallInput,
)
Expand All @@ -61,7 +133,10 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("Bernstein-Yang invert, U256", |b| {
b.iter_batched(
|| {
let x = MontyForm::new(&U256::random(&mut OsRng), params);
let x = MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
);
let inverter = x.params().precompute_inverter();
(x, inverter)
},
Expand All @@ -73,8 +148,14 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("multiplication, U256*U256", |b| {
b.iter_batched(
|| {
let x = MontyForm::new(&U256::random(&mut OsRng), params);
let y = MontyForm::new(&U256::random(&mut OsRng), params);
let x = MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
);
let y = MontyForm::new(
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
params,
);
(x, y)
},
|(x, y)| black_box(x * y),
Expand All @@ -85,9 +166,10 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("modpow, U256^U256", |b| {
b.iter_batched(
|| {
let x = U256::random(&mut OsRng);
let x = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref());
let x_m = MontyForm::new(&x, params);
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
let p = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref())
| (U256::ONE << (U256::BITS - 1));
(x_m, p)
},
|(x, p)| black_box(x.pow(&p)),
Expand All @@ -104,9 +186,10 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
|| {
let bases_and_exponents: Vec<(MontyForm<{ U256::LIMBS }>, U256)> = (1..=i)
.map(|_| {
let x = U256::random(&mut OsRng);
let x = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref());
let x_m = MontyForm::new(&x, params);
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
let p = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref())
| (U256::ONE << (U256::BITS - 1));
(x_m, p)
})
.collect();
Expand Down
6 changes: 6 additions & 0 deletions src/limb/shl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ impl Limb {
pub const fn shl(self, shift: u32) -> Self {
Limb(self.0 << shift)
}

/// Computes `self << 1` and return the result and the carry (0 or 1).
#[inline(always)]
pub(crate) const fn shl1(self) -> (Self, Self) {
(Self(self.0 << 1), Self(self.0 >> Self::HI_BIT))
}
}

macro_rules! impl_shl {
Expand Down
Loading

0 comments on commit 20059a7

Please sign in to comment.