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.
///
/// 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"))]
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>,
J: IntoIterator<Item = &'b ExtendedPoint>
J: IntoIterator<Item = &'b edwards::ExtendedPoint>
{
use traits::select_precomputed_point;
//assert_eq!(scalars.len(), points.len());
let lookup_tables: Vec<_> = points.into_iter()
.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]
let mut lookup_table: [ExtendedPoint; 8] = [*P_i; 8];
.map(|P| {
let P = ExtendedPoint::from(*P);
// 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 {
lookup_table[i+1] = P_i + &lookup_table[i];
lookup_table[i+1] = &P + &lookup_table[i];
}
lookup_table
}).collect();
@ -448,7 +453,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint
Q = &Q + &R_i;
}
}
Q
Q.into()
}
pub mod vartime {
@ -464,10 +469,12 @@ pub mod vartime {
/// error to call this function with two vectors of different lengths.
///
/// XXX need to clear memory
///
/// XXX see note on consttime multiscalar mul
#[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>,
J: IntoIterator<Item = &'b ExtendedPoint>
J: IntoIterator<Item = &'b edwards::ExtendedPoint>
{
//assert_eq!(scalars.len(), points.len());
@ -475,9 +482,10 @@ pub mod vartime {
.map(|c| c.non_adjacent_form()).collect();
let odd_multiples: Vec<_> = points.into_iter()
.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 mut lookup_table: [ExtendedPoint; 8] = [*P; 8];
let mut lookup_table: [ExtendedPoint; 8] = [P; 8];
for i in 0..7 {
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_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(),
edwards::ExtendedPoint::from(R_multiscalar).compress());
R_multiscalar.compress());
}
mod vartime {
@ -754,10 +762,10 @@ mod test {
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(),
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();
// Create 10 points (by doing scalar mults)
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));
}
@ -850,7 +858,7 @@ mod bench {
// Create 2 random scalars
let s1 = 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;
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();
// Create 10 points (by doing scalar mults)
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));
}

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>,
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()
.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]
let mut lookup_table = [P_i.to_projective_niels(); 8];
for j in 0..7 {
lookup_table[j+1] = (P_i + &lookup_table[j])
.to_extended().to_projective_niels();
}
lookup_table
}).collect();
// Setting s_i = i-th scalar, compute
//
// s_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`.
let scalar_digits_list: Vec<_> = scalars.into_iter()
.map(|c| c.to_radix_16()).collect();
// Compute s_1*P_1 + ... + s_n*P_n: since
//
// s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^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})...)
//
// we have the two-dimensional sum
//
// 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})...)
// ...
// + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...)
//
// We sum column-wise top-to-bottom, then right-to-left,
// multiplying by 16 only once per column.
//
// This provides the speedup over doing n independent scalar
// mults: we perform 63 multiplications by 16 instead of 63*n
// multiplications, saving 252*(n-1) doublings.
let mut Q = ExtendedPoint::identity();
// XXX this impl makes no effort to be cache-aware; maybe it could be improved?
for j in (0..64).rev() {
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();
}
edwards_avx2::multiscalar_mult(scalars, points)
}
// Otherwise, proceed as normal:
#[cfg(not(all(target_feature = "avx2", feature = "avx2_backend")))] {
//assert_eq!(scalars.len(), points.len());
let lookup_tables: Vec<_> = points.into_iter()
.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]
let mut lookup_table = [P_i.to_projective_niels(); 8];
for j in 0..7 {
lookup_table[j+1] = (P_i + &lookup_table[j])
.to_extended().to_projective_niels();
}
lookup_table
}).collect();
// Setting s_i = i-th scalar, compute
//
// s_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`.
let scalar_digits_list: Vec<_> = scalars.into_iter()
.map(|c| c.to_radix_16()).collect();
// Compute s_1*P_1 + ... + s_n*P_n: since
//
// s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^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})...)
//
// we have the two-dimensional sum
//
// 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})...)
// ...
// + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...)
//
// We sum column-wise top-to-bottom, then right-to-left,
// multiplying by 16 only once per column.
//
// This provides the speedup over doing n independent scalar
// mults: we perform 63 multiplications by 16 instead of 63*n
// multiplications, saving 252*(n-1) doublings.
let mut Q = ExtendedPoint::identity();
// XXX this impl makes no effort to be cache-aware; maybe it could be improved?
for j in (0..64).rev() {
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
@ -801,30 +810,39 @@ pub mod vartime {
where I: IntoIterator<Item = &'a Scalar>,
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()
.map(|c| c.non_adjacent_form()).collect();
let odd_multiples: Vec<_> = points.into_iter()
.map(|P| OddMultiples::create(P)).collect();
edwards_avx2::vartime::multiscalar_mult(scalars, points)
}
// Otherwise, proceed as normal:
#[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 t = r.double();
let mut r = ProjectivePoint::identity();
for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) {
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];
for i in (0..255).rev() {
let mut t = r.double();
for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) {
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