Skip to content

Commit 20059a7

Browse files
Montgomery form arithmetic improvements (#664)
Signed-off-by: Andrew Whitehead <[email protected]>
1 parent 6cb2d09 commit 20059a7

File tree

17 files changed

+429
-44
lines changed

17 files changed

+429
-44
lines changed

benches/boxed_monty.rs

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,69 @@ fn to_biguint(uint: &BoxedUint) -> BigUint {
1919
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
2020
let params = BoxedMontyParams::new(Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS));
2121

22-
group.bench_function("invert, 4096-bit", |b| {
22+
group.bench_function(format!("add, {UINT_BITS}-bit"), |b| {
23+
b.iter_batched(
24+
|| {
25+
let a = BoxedMontyForm::new(
26+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
27+
params.clone(),
28+
);
29+
let b = BoxedMontyForm::new(
30+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
31+
params.clone(),
32+
);
33+
(a, b)
34+
},
35+
|(a, b)| black_box(a).add(&black_box(b)),
36+
BatchSize::SmallInput,
37+
)
38+
});
39+
40+
group.bench_function(format!("double, {UINT_BITS}-bit"), |b| {
41+
b.iter_batched(
42+
|| {
43+
BoxedMontyForm::new(
44+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
45+
params.clone(),
46+
)
47+
},
48+
|a| black_box(a).double(),
49+
BatchSize::SmallInput,
50+
)
51+
});
52+
53+
group.bench_function(format!("sub, {UINT_BITS}-bit"), |b| {
54+
b.iter_batched(
55+
|| {
56+
let a = BoxedMontyForm::new(
57+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
58+
params.clone(),
59+
);
60+
let b = BoxedMontyForm::new(
61+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
62+
params.clone(),
63+
);
64+
(a, b)
65+
},
66+
|(a, b)| black_box(a).sub(&black_box(b)),
67+
BatchSize::SmallInput,
68+
)
69+
});
70+
71+
group.bench_function(format!("neg, {UINT_BITS}-bit"), |b| {
72+
b.iter_batched(
73+
|| {
74+
BoxedMontyForm::new(
75+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
76+
params.clone(),
77+
)
78+
},
79+
|a| black_box(a).neg(),
80+
BatchSize::SmallInput,
81+
)
82+
});
83+
84+
group.bench_function(format!("invert, {UINT_BITS}-bit"), |b| {
2385
b.iter_batched(
2486
|| {
2587
BoxedMontyForm::new(
@@ -36,11 +98,11 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
3698
b.iter_batched(
3799
|| {
38100
let x = BoxedMontyForm::new(
39-
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
101+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
40102
params.clone(),
41103
);
42104
let y = BoxedMontyForm::new(
43-
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
105+
BoxedUint::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
44106
params.clone(),
45107
);
46108
(x, y)

benches/const_monty.rs

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use criterion::{
22
black_box, criterion_group, criterion_main, measurement::Measurement, BatchSize,
33
BenchmarkGroup, Criterion,
44
};
5-
use crypto_bigint::{impl_modulus, modular::ConstMontyParams, Invert, Inverter, Random, U256};
5+
use crypto_bigint::{
6+
impl_modulus, modular::ConstMontyParams, Invert, Inverter, Random, RandomMod, U256,
7+
};
68
use rand_core::OsRng;
79

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

2830
group.bench_function("ConstMontyForm retrieve", |b| {
2931
b.iter_batched(
30-
|| ConstMontyForm::new(&U256::random(&mut OsRng)),
32+
|| ConstMontyForm::random(&mut OsRng),
3133
|x| black_box(x.retrieve()),
3234
BatchSize::SmallInput,
3335
)
3436
});
3537
}
3638

3739
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
40+
group.bench_function("add, U256", |b| {
41+
b.iter_batched(
42+
|| {
43+
let a = ConstMontyForm::random(&mut OsRng);
44+
let b = ConstMontyForm::random(&mut OsRng);
45+
(a, b)
46+
},
47+
|(a, b)| black_box(a).add(&black_box(b)),
48+
BatchSize::SmallInput,
49+
)
50+
});
51+
52+
group.bench_function("double, U256", |b| {
53+
b.iter_batched(
54+
|| ConstMontyForm::random(&mut OsRng),
55+
|a| black_box(a).double(),
56+
BatchSize::SmallInput,
57+
)
58+
});
59+
60+
group.bench_function("sub, U256", |b| {
61+
b.iter_batched(
62+
|| {
63+
let a = ConstMontyForm::random(&mut OsRng);
64+
let b = ConstMontyForm::random(&mut OsRng);
65+
(a, b)
66+
},
67+
|(a, b)| black_box(a).sub(&black_box(b)),
68+
BatchSize::SmallInput,
69+
)
70+
});
71+
72+
group.bench_function("neg, U256", |b| {
73+
b.iter_batched(
74+
|| ConstMontyForm::random(&mut OsRng),
75+
|a| black_box(a).neg(),
76+
BatchSize::SmallInput,
77+
)
78+
});
79+
3880
group.bench_function("invert, U256", |b| {
3981
b.iter_batched(
40-
|| ConstMontyForm::new(&U256::random(&mut OsRng)),
82+
|| ConstMontyForm::random(&mut OsRng),
4183
|x| black_box(x).invert(),
4284
BatchSize::SmallInput,
4385
)
@@ -46,7 +88,7 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
4688
group.bench_function("Bernstein-Yang invert, U256", |b| {
4789
b.iter_batched(
4890
|| {
49-
let x = ConstMontyForm::new(&U256::random(&mut OsRng));
91+
let x = ConstMontyForm::random(&mut OsRng);
5092
let inverter = Modulus::precompute_inverter();
5193
(x, inverter)
5294
},
@@ -58,20 +100,27 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
58100
group.bench_function("multiplication, U256*U256", |b| {
59101
b.iter_batched(
60102
|| {
61-
let x = ConstMontyForm::new(&U256::random(&mut OsRng));
62-
let y = ConstMontyForm::new(&U256::random(&mut OsRng));
103+
let x = ConstMontyForm::random(&mut OsRng);
104+
let y = ConstMontyForm::random(&mut OsRng);
63105
(x, y)
64106
},
65-
|(x, y)| black_box(x * y),
107+
|(x, y)| black_box(x).mul(&black_box(y)),
108+
BatchSize::SmallInput,
109+
)
110+
});
111+
112+
group.bench_function("squaring, U256*U256", |b| {
113+
b.iter_batched(
114+
|| ConstMontyForm::random(&mut OsRng),
115+
|x| black_box(x).square(),
66116
BatchSize::SmallInput,
67117
)
68118
});
69119

70120
group.bench_function("modpow, U256^U256", |b| {
71121
b.iter_batched(
72122
|| {
73-
let x = U256::random(&mut OsRng);
74-
let x_m = ConstMontyForm::new(&x);
123+
let x_m = ConstMontyForm::random(&mut OsRng);
75124
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
76125
(x_m, p)
77126
},
@@ -89,8 +138,7 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
89138
|| {
90139
let bases_and_exponents: Vec<(ConstMontyForm, U256)> = (1..=i)
91140
.map(|_| {
92-
let x = U256::random(&mut OsRng);
93-
let x_m = ConstMontyForm::new(&x);
141+
let x_m = ConstMontyForm::random(&mut OsRng);
94142
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
95143
(x_m, p)
96144
})

benches/monty.rs

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use criterion::{
44
};
55
use crypto_bigint::{
66
modular::{MontyForm, MontyParams},
7-
Invert, Inverter, Odd, PrecomputeInverter, Random, U256,
7+
Invert, Inverter, Odd, PrecomputeInverter, Random, RandomMod, U256,
88
};
99
use rand_core::OsRng;
1010

@@ -31,7 +31,7 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
3131
let params = MontyParams::new_vartime(Odd::<U256>::random(&mut OsRng));
3232
group.bench_function("MontyForm::new", |b| {
3333
b.iter_batched(
34-
|| Odd::<U256>::random(&mut OsRng),
34+
|| U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
3535
|x| black_box(MontyForm::new(&x, params)),
3636
BatchSize::SmallInput,
3737
)
@@ -40,7 +40,12 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
4040
let params = MontyParams::new_vartime(Odd::<U256>::random(&mut OsRng));
4141
group.bench_function("MontyForm retrieve", |b| {
4242
b.iter_batched(
43-
|| MontyForm::new(&U256::random(&mut OsRng), params),
43+
|| {
44+
MontyForm::new(
45+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
46+
params,
47+
)
48+
},
4449
|x| black_box(x.retrieve()),
4550
BatchSize::SmallInput,
4651
)
@@ -50,9 +55,76 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
5055
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
5156
let params = MontyParams::new_vartime(Odd::<U256>::random(&mut OsRng));
5257

58+
group.bench_function("add, U256", |b| {
59+
b.iter_batched(
60+
|| {
61+
let a = MontyForm::new(
62+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
63+
params,
64+
);
65+
let b = MontyForm::new(
66+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
67+
params,
68+
);
69+
(a, b)
70+
},
71+
|(a, b)| black_box(a).add(&black_box(b)),
72+
BatchSize::SmallInput,
73+
)
74+
});
75+
76+
group.bench_function("double, U256", |b| {
77+
b.iter_batched(
78+
|| {
79+
MontyForm::new(
80+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
81+
params,
82+
)
83+
},
84+
|a| black_box(a).double(),
85+
BatchSize::SmallInput,
86+
)
87+
});
88+
89+
group.bench_function("sub, U256", |b| {
90+
b.iter_batched(
91+
|| {
92+
let a = MontyForm::new(
93+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
94+
params,
95+
);
96+
let b = MontyForm::new(
97+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
98+
params,
99+
);
100+
(a, b)
101+
},
102+
|(a, b)| black_box(a).sub(&black_box(b)),
103+
BatchSize::SmallInput,
104+
)
105+
});
106+
107+
group.bench_function("neg, U256", |b| {
108+
b.iter_batched(
109+
|| {
110+
MontyForm::new(
111+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
112+
params,
113+
)
114+
},
115+
|a| black_box(a).neg(),
116+
BatchSize::SmallInput,
117+
)
118+
});
119+
53120
group.bench_function("invert, U256", |b| {
54121
b.iter_batched(
55-
|| MontyForm::new(&U256::random(&mut OsRng), params),
122+
|| {
123+
MontyForm::new(
124+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
125+
params,
126+
)
127+
},
56128
|x| black_box(x).invert(),
57129
BatchSize::SmallInput,
58130
)
@@ -61,7 +133,10 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
61133
group.bench_function("Bernstein-Yang invert, U256", |b| {
62134
b.iter_batched(
63135
|| {
64-
let x = MontyForm::new(&U256::random(&mut OsRng), params);
136+
let x = MontyForm::new(
137+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
138+
params,
139+
);
65140
let inverter = x.params().precompute_inverter();
66141
(x, inverter)
67142
},
@@ -73,8 +148,14 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
73148
group.bench_function("multiplication, U256*U256", |b| {
74149
b.iter_batched(
75150
|| {
76-
let x = MontyForm::new(&U256::random(&mut OsRng), params);
77-
let y = MontyForm::new(&U256::random(&mut OsRng), params);
151+
let x = MontyForm::new(
152+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
153+
params,
154+
);
155+
let y = MontyForm::new(
156+
&U256::random_mod(&mut OsRng, params.modulus().as_nz_ref()),
157+
params,
158+
);
78159
(x, y)
79160
},
80161
|(x, y)| black_box(x * y),
@@ -85,9 +166,10 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
85166
group.bench_function("modpow, U256^U256", |b| {
86167
b.iter_batched(
87168
|| {
88-
let x = U256::random(&mut OsRng);
169+
let x = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref());
89170
let x_m = MontyForm::new(&x, params);
90-
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
171+
let p = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref())
172+
| (U256::ONE << (U256::BITS - 1));
91173
(x_m, p)
92174
},
93175
|(x, p)| black_box(x.pow(&p)),
@@ -104,9 +186,10 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
104186
|| {
105187
let bases_and_exponents: Vec<(MontyForm<{ U256::LIMBS }>, U256)> = (1..=i)
106188
.map(|_| {
107-
let x = U256::random(&mut OsRng);
189+
let x = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref());
108190
let x_m = MontyForm::new(&x, params);
109-
let p = U256::random(&mut OsRng) | (U256::ONE << (U256::BITS - 1));
191+
let p = U256::random_mod(&mut OsRng, params.modulus().as_nz_ref())
192+
| (U256::ONE << (U256::BITS - 1));
110193
(x_m, p)
111194
})
112195
.collect();

src/limb/shl.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ impl Limb {
1111
pub const fn shl(self, shift: u32) -> Self {
1212
Limb(self.0 << shift)
1313
}
14+
15+
/// Computes `self << 1` and return the result and the carry (0 or 1).
16+
#[inline(always)]
17+
pub(crate) const fn shl1(self) -> (Self, Self) {
18+
(Self(self.0 << 1), Self(self.0 >> Self::HI_BIT))
19+
}
1420
}
1521

1622
macro_rules! impl_shl {

0 commit comments

Comments
 (0)