Connect multiscalar_mult to the AVX2 backend

This commit is contained in:
Henry de Valence 2017-11-21 12:27:32 -08:00
parent 912fc5d412
commit b5305b4e30
2 changed files with 112 additions and 86 deletions

View file

@ -391,20 +391,25 @@ impl EdwardsBasepointTable {
/// error to call this function with two vectors of different lengths. /// error to call this function with two vectors of different lengths.
/// ///
/// XXX need to clear memory /// XXX need to clear memory
///
/// XXX this takes `edwards::ExtendedPoints` because we have to alloc scratch space here anyways,
/// and we need some space to store the converted points, so we may as well do the conversion here.
/// maybe there's a better way to avoid code duplication...
#[cfg(any(feature = "alloc", feature = "std"))] #[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::ExtendedPoint
where I: IntoIterator<Item = &'a Scalar>, where I: IntoIterator<Item = &'a Scalar>,
J: IntoIterator<Item = &'b ExtendedPoint> J: IntoIterator<Item = &'b edwards::ExtendedPoint>
{ {
use traits::select_precomputed_point; use traits::select_precomputed_point;
//assert_eq!(scalars.len(), points.len()); //assert_eq!(scalars.len(), points.len());
let lookup_tables: Vec<_> = points.into_iter() let lookup_tables: Vec<_> = points.into_iter()
.map(|P_i| { .map(|P| {
// Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] let P = ExtendedPoint::from(*P);
let mut lookup_table: [ExtendedPoint; 8] = [*P_i; 8]; // Construct a lookup table of [P,2*P,3*P,4*P,5*P,6*P,7*P]
let mut lookup_table: [ExtendedPoint; 8] = [P; 8];
for i in 0..7 { for i in 0..7 {
lookup_table[i+1] = P_i + &lookup_table[i]; lookup_table[i+1] = &P + &lookup_table[i];
} }
lookup_table lookup_table
}).collect(); }).collect();
@ -448,7 +453,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint
Q = &Q + &R_i; Q = &Q + &R_i;
} }
} }
Q Q.into()
} }
pub mod vartime { pub mod vartime {
@ -464,10 +469,12 @@ pub mod vartime {
/// error to call this function with two vectors of different lengths. /// error to call this function with two vectors of different lengths.
/// ///
/// XXX need to clear memory /// XXX need to clear memory
///
/// XXX see note on consttime multiscalar mul
#[cfg(any(feature = "alloc", feature = "std"))] #[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::ExtendedPoint
where I: IntoIterator<Item = &'a Scalar>, where I: IntoIterator<Item = &'a Scalar>,
J: IntoIterator<Item = &'b ExtendedPoint> J: IntoIterator<Item = &'b edwards::ExtendedPoint>
{ {
//assert_eq!(scalars.len(), points.len()); //assert_eq!(scalars.len(), points.len());
@ -475,9 +482,10 @@ pub mod vartime {
.map(|c| c.non_adjacent_form()).collect(); .map(|c| c.non_adjacent_form()).collect();
let odd_multiples: Vec<_> = points.into_iter() let odd_multiples: Vec<_> = points.into_iter()
.map(|P| { .map(|P| {
// Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] let P = ExtendedPoint::from(*P);
// Construct a lookup table of [P,2*P,3*P,4*P,5*P,6*P,7*P]
let P2 = P.double(); let P2 = P.double();
let mut lookup_table: [ExtendedPoint; 8] = [*P; 8]; let mut lookup_table: [ExtendedPoint; 8] = [P; 8];
for i in 0..7 { for i in 0..7 {
lookup_table[i+1] = &P2 + &lookup_table[i]; lookup_table[i+1] = &P2 + &lookup_table[i];
} }
@ -498,7 +506,7 @@ pub mod vartime {
} }
} }
} }
Q Q.into()
} }
} }
@ -734,10 +742,10 @@ mod test {
let R = &(&P1 * &s1) + &(&P2 * &s2); let R = &(&P1 * &s1) + &(&P2 * &s2);
let R_multiscalar = multiscalar_mult(&[s1, s2], &[P1, P2]); let R_multiscalar = multiscalar_mult(&[s1, s2], &[P1.into(), P2.into()]);
assert_eq!(edwards::ExtendedPoint::from(R).compress(), assert_eq!(edwards::ExtendedPoint::from(R).compress(),
edwards::ExtendedPoint::from(R_multiscalar).compress()); R_multiscalar.compress());
} }
mod vartime { mod vartime {
@ -754,10 +762,10 @@ mod test {
let R = &(&P1 * &s1) + &(&P2 * &s2); let R = &(&P1 * &s1) + &(&P2 * &s2);
let R_multiscalar = vartime::multiscalar_mult(&[s1, s2], &[P1, P2]); let R_multiscalar = vartime::multiscalar_mult(&[s1, s2], &[P1.into(), P2.into()]);
assert_eq!(edwards::ExtendedPoint::from(R).compress(), assert_eq!(edwards::ExtendedPoint::from(R).compress(),
edwards::ExtendedPoint::from(R_multiscalar).compress()); R_multiscalar.compress());
} }
} }
} }
@ -835,7 +843,7 @@ mod bench {
let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect();
// Create 10 points (by doing scalar mults) // Create 10 points (by doing scalar mults)
let B = &constants::ED25519_BASEPOINT_POINT; let B = &constants::ED25519_BASEPOINT_POINT;
let points: Vec<_> = scalars.iter().map(|s| ExtendedPoint::from(B * &s)).collect(); let points: Vec<_> = scalars.iter().map(|s| B * &s).collect();
b.iter(|| multiscalar_mult(&scalars, &points)); b.iter(|| multiscalar_mult(&scalars, &points));
} }
@ -850,7 +858,7 @@ mod bench {
// Create 2 random scalars // Create 2 random scalars
let s1 = Scalar::random(&mut csprng); let s1 = Scalar::random(&mut csprng);
let s2 = Scalar::random(&mut csprng); let s2 = Scalar::random(&mut csprng);
let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); let B = constants::ED25519_BASEPOINT_POINT;
let P = &B * &s1; let P = &B * &s1;
b.iter(|| vartime::multiscalar_mult(&[s1, s2], &[B, P])); b.iter(|| vartime::multiscalar_mult(&[s1, s2], &[B, P]));
@ -863,7 +871,7 @@ mod bench {
let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect();
// Create 10 points (by doing scalar mults) // Create 10 points (by doing scalar mults)
let B = &constants::ED25519_BASEPOINT_POINT; let B = &constants::ED25519_BASEPOINT_POINT;
let points: Vec<_> = scalars.iter().map(|s| ExtendedPoint::from(B * &s)).collect(); let points: Vec<_> = scalars.iter().map(|s| B * &s).collect();
b.iter(|| vartime::multiscalar_mult(&scalars, &points)); b.iter(|| vartime::multiscalar_mult(&scalars, &points));
} }

View file

@ -510,59 +510,68 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint
where I: IntoIterator<Item = &'a Scalar>, where I: IntoIterator<Item = &'a Scalar>,
J: IntoIterator<Item = &'b ExtendedPoint> J: IntoIterator<Item = &'b ExtendedPoint>
{ {
//assert_eq!(scalars.len(), points.len()); // If we built with AVX2, use the AVX2 backend.
#[cfg(all(target_feature = "avx2", feature = "avx2_backend"))] {
use backend::avx2::edwards as edwards_avx2;
let lookup_tables: Vec<_> = points.into_iter() edwards_avx2::multiscalar_mult(scalars, points)
.map(|P_i| { }
// Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] // Otherwise, proceed as normal:
let mut lookup_table = [P_i.to_projective_niels(); 8]; #[cfg(not(all(target_feature = "avx2", feature = "avx2_backend")))] {
for j in 0..7 { //assert_eq!(scalars.len(), points.len());
lookup_table[j+1] = (P_i + &lookup_table[j])
.to_extended().to_projective_niels(); let lookup_tables: Vec<_> = points.into_iter()
} .map(|P_i| {
lookup_table // Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i]
}).collect(); let mut lookup_table = [P_i.to_projective_niels(); 8];
for j in 0..7 {
// Setting s_i = i-th scalar, compute lookup_table[j+1] = (P_i + &lookup_table[j])
// .to_extended().to_projective_niels();
// s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, }
// lookup_table
// with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. }).collect();
let scalar_digits_list: Vec<_> = scalars.into_iter()
.map(|c| c.to_radix_16()).collect(); // Setting s_i = i-th scalar, compute
//
// Compute s_1*P_1 + ... + s_n*P_n: since // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63,
// //
// s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63) // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`.
// s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63 let scalar_digits_list: Vec<_> = scalars.into_iter()
// s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...) .map(|c| c.to_radix_16()).collect();
//
// we have the two-dimensional sum // Compute s_1*P_1 + ... + s_n*P_n: since
// //
// s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...) // s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63)
// + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...) // s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63
// ... // s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...)
// + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...) //
// // we have the two-dimensional sum
// We sum column-wise top-to-bottom, then right-to-left, //
// multiplying by 16 only once per column. // s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...)
// // + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...)
// This provides the speedup over doing n independent scalar // ...
// mults: we perform 63 multiplications by 16 instead of 63*n // + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...)
// multiplications, saving 252*(n-1) doublings. //
let mut Q = ExtendedPoint::identity(); // We sum column-wise top-to-bottom, then right-to-left,
// XXX this impl makes no effort to be cache-aware; maybe it could be improved? // multiplying by 16 only once per column.
for j in (0..64).rev() { //
Q = Q.mult_by_pow_2(4); // This provides the speedup over doing n independent scalar
let it = scalar_digits_list.iter().zip(lookup_tables.iter()); // mults: we perform 63 multiplications by 16 instead of 63*n
for (s_i, lookup_table_i) in it { // multiplications, saving 252*(n-1) doublings.
// R_i = s_{i,j} * P_i let mut Q = ExtendedPoint::identity();
let R_i = select_precomputed_point(s_i[j], lookup_table_i); // XXX this impl makes no effort to be cache-aware; maybe it could be improved?
// Q = Q + R_i for j in (0..64).rev() {
Q = (&Q + &R_i).to_extended(); Q = Q.mult_by_pow_2(4);
} let it = scalar_digits_list.iter().zip(lookup_tables.iter());
for (s_i, lookup_table_i) in it {
// R_i = s_{i,j} * P_i
let R_i = select_precomputed_point(s_i[j], lookup_table_i);
// Q = Q + R_i
Q = (&Q + &R_i).to_extended();
}
}
Q
} }
Q
} }
/// A precomputed table of multiples of a basepoint, for accelerating /// A precomputed table of multiples of a basepoint, for accelerating
@ -801,30 +810,39 @@ pub mod vartime {
where I: IntoIterator<Item = &'a Scalar>, where I: IntoIterator<Item = &'a Scalar>,
J: IntoIterator<Item = &'b ExtendedPoint> J: IntoIterator<Item = &'b ExtendedPoint>
{ {
//assert_eq!(scalars.len(), points.len()); // If we built with AVX2, use the AVX2 backend.
#[cfg(all(target_feature = "avx2", feature = "avx2_backend"))] {
use backend::avx2::edwards as edwards_avx2;
let nafs: Vec<_> = scalars.into_iter() edwards_avx2::vartime::multiscalar_mult(scalars, points)
.map(|c| c.non_adjacent_form()).collect(); }
let odd_multiples: Vec<_> = points.into_iter() // Otherwise, proceed as normal:
.map(|P| OddMultiples::create(P)).collect(); #[cfg(not(all(target_feature = "avx2", feature = "avx2_backend")))] {
//assert_eq!(scalars.len(), points.len());
let mut r = ProjectivePoint::identity(); let nafs: Vec<_> = scalars.into_iter()
.map(|c| c.non_adjacent_form()).collect();
let odd_multiples: Vec<_> = points.into_iter()
.map(|P| OddMultiples::create(P)).collect();
for i in (0..255).rev() { let mut r = ProjectivePoint::identity();
let mut t = r.double();
for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) { for i in (0..255).rev() {
if naf[i] > 0 { let mut t = r.double();
t = &t.to_extended() + &odd_multiple[( naf[i]/2) as usize];
} else if naf[i] < 0 { for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) {
t = &t.to_extended() - &odd_multiple[(-naf[i]/2) as usize]; if naf[i] > 0 {
t = &t.to_extended() + &odd_multiple[( naf[i]/2) as usize];
} else if naf[i] < 0 {
t = &t.to_extended() - &odd_multiple[(-naf[i]/2) as usize];
}
} }
r = t.to_projective();
} }
r = t.to_projective(); r.to_extended()
} }
r.to_extended()
} }
/// Given a point \\(A\\) and scalars \\(a\\) and \\(b\\), compute the point /// Given a point \\(A\\) and scalars \\(a\\) and \\(b\\), compute the point