Skip to content

Commit 713cf40

Browse files
committed
Fork Half impl in two different implementations.
- `fast_even_half` should be used for even `FieldElements` only and performs almost 4x faster than the `Half` trait impl. - `Half` trait should be used when we don't know if the `FieldElement` will be even or not. - Implemented benchmarks for both functions that implement the same operation. - Modified `mod_sqrt()` implementation to work with the fast `half` implementation.
1 parent 13d484f commit 713cf40

File tree

2 files changed

+48
-12
lines changed

2 files changed

+48
-12
lines changed

benchmarks/dusk_benchmarks.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,9 +508,9 @@ mod comparaisons {
508508
]),
509509
});
510510

511-
/// `D = 904625697166532776746648320197686575422163851717637391703244652875051672039`
511+
/// `D = 904625697166532776746648320197686575422163851717637391703244652875051672038`
512512
pub static D: Scalar = Scalar([
513-
2766226127823335,
513+
2766226127823334,
514514
4237835465749098,
515515
4503599626623787,
516516
4503599627370493,
@@ -520,6 +520,16 @@ mod comparaisons {
520520
pub fn bench_point_ops_impl(c: &mut Criterion) {
521521
let i = P1_EXTENDED;
522522
let mul = (RISTRETTO_BASEPOINT, D);
523+
524+
let mut group = c.benchmark_group("Half");
525+
526+
group.bench_with_input(BenchmarkId::new("Fast Even Half", "Fixed even FieldElement"), &i,
527+
|b, &i| b.iter(|| i.0.T.old_half()));
528+
group.bench_with_input(BenchmarkId::new("Constant usage impl", "Fixed even FieldElement"), &i,
529+
|b, &i| b.iter(|| i.0.T.half()));
530+
531+
group.finish();
532+
523533

524534
// Equalty
525535
let mut group = c.benchmark_group("Equalty");

src/backend/u64/field.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,6 @@ impl<'a> Square for &'a FieldElement {
319319
impl<'a> Half for &'a FieldElement {
320320
type Output = FieldElement;
321321
/// Give the half of the FieldElement value (mod l).
322-
///
323-
/// This function SHOULD ONLY be used with even
324-
/// `FieldElements` otherways, can produce erroneus
325-
/// results.
326322
fn half(self) -> FieldElement {
327323
self * &constants::INVERSE_MOD_TWO
328324
}
@@ -337,6 +333,7 @@ impl<'a, 'b> Pow<&'b FieldElement> for &'a FieldElement {
337333
///
338334
/// Schneier, Bruce (1996). Applied Cryptography: Protocols,
339335
/// Algorithms, and Source Code in C, Second Edition (2nd ed.).
336+
/// TODO: Review the while with the different half implementations.
340337
fn pow(self, exp: &'b FieldElement) -> FieldElement {
341338
let (zero, one) = (FieldElement::zero(), FieldElement::one());
342339
let mut base = *self;
@@ -405,15 +402,15 @@ impl<'a> ModSqrt for &'a FieldElement {
405402
let mut s = zero;
406403
while q.is_even() {
407404
s = s + one;
408-
q = q.half();
405+
q = q.fast_even_half();
409406
}
410407

411408
// Select a z which is a quadratic non resudue modulo p.
412409
// We pre-computed it so we know that 6 isn't QR.
413410
let mut c = six.pow(&q);
414411

415412
// Search for a solution.
416-
let mut x = self.pow(&(q + one).half());
413+
let mut x = self.pow(&(q + one).fast_even_half());
417414
let mut t = self.pow(&q);
418415
let mut m = s;
419416

@@ -514,6 +511,7 @@ fn m(x: u64, y: u64) -> u128 {
514511
}
515512

516513
impl FieldElement {
514+
517515
/// Construct zero.
518516
pub const fn zero() -> FieldElement {
519517
FieldElement([0, 0, 0, 0, 0])
@@ -670,6 +668,33 @@ impl FieldElement {
670668
res
671669
}
672670

671+
/// Returns the half of an **EVEN** `FieldElement`.
672+
///
673+
/// This function performs almost 4x faster than the
674+
/// `Half` implementation but SHOULD be used carefully.
675+
pub fn fast_even_half(self) -> FieldElement {
676+
assert!(self.is_even());
677+
let mut res = self;
678+
let mut remainder = 0u64;
679+
for i in (0..5).rev() {
680+
res[i] = res[i] + remainder;
681+
match (res[i] == 1, res[i].is_even()) {
682+
(true, _) => {
683+
remainder = 4503599627370496u64;
684+
}
685+
(_, false) => {
686+
res[i] = res[i] - 1u64;
687+
remainder = 4503599627370496u64;
688+
}
689+
(_, true) => {
690+
remainder = 0;
691+
}
692+
}
693+
res[i] >>= 1;
694+
}
695+
res
696+
}
697+
673698
/// Given a FieldElement, this function evaluates if it is a quadratic
674699
/// residue (mod l).
675700
///
@@ -683,6 +708,7 @@ impl FieldElement {
683708
///
684709
/// `0` -> `Input (mod l) == 0`. Not implemented since you can't pass
685710
/// an input which is multiple of `FIELD_L`.
711+
/// TODO: Refactor minus_one.half by a constant.
686712
pub fn legendre_symbol(&self) -> Choice {
687713
let res = self.pow(&FieldElement::minus_one().half());
688714
res.ct_eq(&FieldElement::minus_one()) ^ Choice::from(1u8)
@@ -865,25 +891,25 @@ impl FieldElement {
865891
match (u.is_even(), v.is_even(), u > v, v >= u) {
866892
// u is even
867893
(true, _, _, _) => {
868-
u = u.half();
894+
u = u.fast_even_half();
869895
s = s * two;
870896
}
871897
// u isn't even but v is even
872898
(false, true, _, _) => {
873-
v = v.half();
899+
v = v.fast_even_half();
874900
r = r * two;
875901
}
876902
// u and v aren't even and u > v
877903
(false, false, true, _) => {
878904
u = u - v;
879-
u = u.half();
905+
u = u.fast_even_half();
880906
r = r + s;
881907
s = s * two;
882908
}
883909
// u and v aren't even and v > u
884910
(false, false, false, true) => {
885911
v = v - u;
886-
v = v.half();
912+
v = v.fast_even_half();
887913
s = r + s;
888914
r = r * two;
889915
}

0 commit comments

Comments
 (0)