ed25519: precompute AVX2 basepoint-128 table (#25)

Add a static AVX2 NafLookupTable5<CachedPoint> for B * 2^128 and use it from the vector triple-base verifier path instead of rebuilding that table every verification.

Benchmark notes:

- Ran this repository's Criterion benchmark program, benches/bench.rs, filtering to Single Verification, pinned to CPU 4 with 1s warmup, 2s measurement, and sample size 10.

- local_verify_zebra estimate was 19.382 us, with 95% CI 19.327..19.426 us.

- master measured 20.051 us, with 95% CI 19.938..20.134 us, so this branch was about 3.34% faster in that run.
This commit is contained in:
Edvard Fagerholm 2026-06-08 20:04:02 +03:00 committed by GitHub
parent 7d80488798
commit de07b0a389
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 183 additions and 4 deletions

View file

@ -16,6 +16,7 @@ use crate::backend::vector::packed_simd::u32x8;
use crate::backend::vector::avx2::edwards::{CachedPoint, ExtendedPoint};
use crate::backend::vector::avx2::field::FieldElement2625x4;
use crate::window::NafLookupTable5;
#[cfg(feature = "precomputed-tables")]
use crate::window::NafLookupTable8;
@ -37,6 +38,183 @@ pub(crate) static CACHEDPOINT_IDENTITY: CachedPoint = CachedPoint(FieldElement26
u32x8::new_const(67108863, 0, 33554431, 0, 0, 67108863, 0, 33554431),
]));
/// Odd multiples of `B * 2^128`, where `B` is the Ed25519 basepoint.
pub(crate) static BASEPOINT_128_ODD_LOOKUP_TABLE: NafLookupTable5<CachedPoint> = NafLookupTable5([
CachedPoint(FieldElement2625x4([
u32x8::new_const(
66534737, 44155216, 17527085, 25046477, 50454485, 25275908, 24427949, 19365218,
),
u32x8::new_const(
15981845, 36873404, 8477272, 11176021, 40772245, 51234208, 6101715, 20397724,
),
u32x8::new_const(
35665917, 53574296, 1951442, 2105772, 44081965, 9258257, 14333944, 20637141,
),
u32x8::new_const(
16279886, 64719067, 15989606, 21668724, 19140425, 62032528, 31894001, 14000444,
),
u32x8::new_const(
36500306, 17827321, 19052241, 838783, 31029387, 53863823, 8190087, 6168061,
),
])),
CachedPoint(FieldElement2625x4([
u32x8::new_const(
58297975, 1271791, 26327207, 12780927, 21326724, 55825376, 1881468, 19837539,
),
u32x8::new_const(
41889841, 51572094, 29732198, 3211604, 26179218, 66326610, 23747079, 9590379,
),
u32x8::new_const(
3826490, 25399835, 21583051, 18952539, 65295023, 42750425, 2246330, 32346254,
),
u32x8::new_const(
52190758, 32188098, 24570211, 20678866, 40932593, 8178429, 27967937, 28257661,
),
u32x8::new_const(
30236672, 9815342, 29650636, 24107060, 43660614, 37629440, 12674453, 2006553,
),
])),
CachedPoint(FieldElement2625x4([
u32x8::new_const(
61053967, 64436103, 447268, 25700182, 19255118, 14993526, 899478, 2434175,
),
u32x8::new_const(
35812815, 10060004, 31384605, 25670757, 9561857, 60752897, 21085685, 27549514,
),
u32x8::new_const(
18488525, 64357302, 10815830, 2935874, 29965756, 65776827, 18067405, 29757081,
),
u32x8::new_const(
9151450, 55058906, 9505908, 19318842, 16283246, 36644461, 27208985, 12764280,
),
u32x8::new_const(
935064, 62208992, 28617878, 9642406, 33296477, 20692366, 14092783, 29523038,
),
])),
CachedPoint(FieldElement2625x4([
u32x8::new_const(
43982257, 1870168, 4563592, 25651161, 16953299, 59521203, 32759146, 23740415,
),
u32x8::new_const(
58238384, 34563808, 32443148, 21494201, 4125650, 52067840, 14679425, 9739512,
),
u32x8::new_const(
22799713, 45658991, 31646134, 20985007, 25125757, 30914923, 23225832, 25549240,
),
u32x8::new_const(
57964769, 17027909, 11726313, 16601782, 55202345, 44469180, 19529404, 17872655,
),
u32x8::new_const(
29135281, 26779810, 23911498, 19293297, 15382461, 22625292, 27273113, 11918932,
),
])),
CachedPoint(FieldElement2625x4([
u32x8::new_const(
20356928, 44286797, 22416632, 26923729, 37256426, 26174151, 8631283, 13726744,
),
u32x8::new_const(
15050322, 50131342, 17556728, 23214692, 57865782, 26708371, 14597005, 31489486,
),
u32x8::new_const(
10930030, 52761167, 17112689, 22969526, 21350113, 19294179, 2468751, 28829631,
),
u32x8::new_const(
21428704, 20303938, 30589121, 12662814, 5328394, 19277415, 17102411, 16988972,
),
u32x8::new_const(
18480836, 42343373, 1078384, 27210912, 44126946, 66557826, 6700400, 7552628,
),
])),
CachedPoint(FieldElement2625x4([
u32x8::new_const(
26298451, 31358962, 2410750, 1222016, 6602808, 44151217, 19067066, 20265648,
),
u32x8::new_const(
55489773, 8500419, 18022394, 14157663, 64952743, 30690464, 4133387, 4111644,
),
u32x8::new_const(
62382671, 36329179, 8557628, 5794859, 23898950, 33060808, 8782720, 32408648,
),
u32x8::new_const(
42610331, 38124770, 7134858, 18736387, 7942085, 52164416, 31680501, 29556327,
),
u32x8::new_const(
5234530, 9444407, 25023918, 10390934, 8479340, 52612004, 32163536, 23949516,
),
])),
CachedPoint(FieldElement2625x4([
u32x8::new_const(
23655649, 20966851, 549747, 32424363, 65477828, 28514773, 964955, 24097969,
),
u32x8::new_const(
10190745, 12573645, 17574436, 8670785, 40257066, 24017385, 18903364, 2903730,
),
u32x8::new_const(
65255895, 6342458, 8253517, 21684439, 25311893, 11419775, 17555636, 8477305,
),
u32x8::new_const(
28276837, 53282575, 15032193, 7864586, 13987263, 58062910, 14566708, 20452982,
),
u32x8::new_const(
23280558, 4071800, 13973538, 14314990, 3800625, 49289684, 21965196, 25337015,
),
])),
CachedPoint(FieldElement2625x4([
u32x8::new_const(
12346933, 43640065, 11294461, 3235676, 17615927, 5260789, 4540788, 21895087,
),
u32x8::new_const(
24450060, 4710546, 4707754, 6116116, 59999924, 32574139, 18219955, 32529652,
),
u32x8::new_const(
48787857, 56833627, 595516, 15964297, 18845497, 56029604, 13864576, 18463356,
),
u32x8::new_const(
7335332, 5495540, 24018875, 1219221, 18695805, 51707846, 5336005, 18884320,
),
u32x8::new_const(
27774957, 50806778, 32219720, 17063358, 30418813, 19859422, 2807745, 31952068,
),
])),
]);
#[cfg(all(test, target_feature = "avx2"))]
mod test {
use super::*;
use crate::constants as curve_constants;
fn lanes(limb: u32x8) -> [u32; 8] {
[
limb.extract::<0>(),
limb.extract::<1>(),
limb.extract::<2>(),
limb.extract::<3>(),
limb.extract::<4>(),
limb.extract::<5>(),
limb.extract::<6>(),
limb.extract::<7>(),
]
}
#[test]
fn basepoint_128_odd_lookup_table_matches_runtime_generation() {
let runtime =
NafLookupTable5::<CachedPoint>::from(&curve_constants::ED25519_BASEPOINT_128_POINT);
for (static_point, runtime_point) in BASEPOINT_128_ODD_LOOKUP_TABLE
.0
.iter()
.zip(runtime.0.iter())
{
let static_field = &static_point.0;
let runtime_field = &runtime_point.0;
for (static_limb, runtime_limb) in static_field.0.iter().zip(runtime_field.0.iter()) {
assert_eq!(lanes(*static_limb), lanes(*runtime_limb));
}
}
}
}
/// The low limbs of (2p, 2p, 2p, 2p), so that
/// ```ascii,no_run
/// (2p, 2p, 2p, 2p) = [P_TIMES_2_LO, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI]

View file

@ -15,10 +15,13 @@ pub mod spec {
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::constants::BASEPOINT_128_ODD_LOOKUP_TABLE;
#[cfg(feature = "precomputed-tables")]
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE;
#[cfg(not(feature = "precomputed-tables"))]
use crate::constants;
use crate::edwards::EdwardsPoint;
use crate::scalar::HEEA_MAX_INDEX;
@ -111,10 +114,8 @@ pub mod spec {
#[cfg(not(feature = "precomputed-tables"))]
let table_B = &NafLookupTable5::<CachedPoint>::from(&constants::ED25519_BASEPOINT_POINT);
// B' = B * 2^128 (precomputed constant point)
// TODO: For optimal performance, this should also use the wider lookup table when precomputed-tables is enabled
let table_B_128 =
&NafLookupTable5::<CachedPoint>::from(&constants::ED25519_BASEPOINT_128_POINT);
// B' = B * 2^128.
let table_B_128 = &BASEPOINT_128_ODD_LOOKUP_TABLE;
let mut Q = ExtendedPoint::identity();