use criterion::{criterion_group, criterion_main, Criterion};
#[cfg(feature = "alloc")]
mod ntt_benchmarks {
use criterion::{black_box, BenchmarkId, Criterion};
use dcrypt_algorithms::poly::params::MlDsaParams;
use dcrypt_algorithms::poly::prelude::*;
use dcrypt_algorithms::poly::sampling::{DefaultSamplers, UniformSampler};
use dcrypt_internal::random::ChaCha20Rng;
#[derive(Clone, Debug)]
struct Benchmark3329Params;
impl Modulus for Benchmark3329Params {
const Q: u32 = 3_329;
const N: usize = 256;
const BARRETT_MU: u128 = 10_569_051_393;
const BARRETT_K: u32 = 45;
}
impl NttModulus for Benchmark3329Params {
const ZETA: u32 = 17;
const ZETAS: &'static [u32] = &[];
const N_INV: u32 = 2_385;
const MONT_R: u32 = 1_353;
const NEG_QINV: u32 = 0x9457_0cff;
const PSIS: &'static [u32] = &[];
const INV_PSIS: &'static [u32] = &[];
}
pub fn bench_ml_dsa_forward_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("ml_dsa_ntt");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
let poly = <DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("forward", |b| {
b.iter_batched(
|| poly.clone(),
|mut p| {
p.ntt_inplace().expect("NTT failed");
black_box(p)
},
criterion::BatchSize::SmallInput,
)
});
group.finish();
}
pub fn bench_ml_dsa_inverse_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("ml_dsa_ntt");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
let mut poly = <DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
poly.ntt_inplace().expect("NTT failed");
group.bench_function("inverse", |b| {
b.iter_batched(
|| poly.clone(),
|mut p| {
p.from_ntt_inplace().expect("Inverse NTT failed");
black_box(p)
},
criterion::BatchSize::SmallInput,
)
});
group.finish();
}
pub fn bench_generic_3329_forward_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("generic_3329_ntt");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
let poly =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("forward", |b| {
b.iter_batched(
|| poly.clone(),
|mut p| {
p.ntt_inplace().expect("NTT failed");
black_box(p)
},
criterion::BatchSize::SmallInput,
)
});
group.finish();
}
pub fn bench_generic_3329_inverse_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("generic_3329_ntt");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
let mut poly =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
poly.ntt_inplace().expect("NTT failed");
group.bench_function("inverse", |b| {
b.iter_batched(
|| poly.clone(),
|mut p| {
p.from_ntt_inplace().expect("Inverse NTT failed");
black_box(p)
},
criterion::BatchSize::SmallInput,
)
});
group.finish();
}
pub fn bench_ntt_multiplication(c: &mut Criterion) {
let mut group = c.benchmark_group("ntt_multiplication");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
{
let mut poly_a =
<DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let mut poly_b =
<DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
poly_a.ntt_inplace().expect("NTT failed");
poly_b.ntt_inplace().expect("NTT failed");
group.bench_function("ml_dsa_pointwise", |b| {
b.iter(|| {
let result = poly_a.ntt_mul(&poly_b);
black_box(result)
})
});
}
{
let mut poly_a =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let mut poly_b =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
poly_a.ntt_inplace().expect("NTT failed");
poly_b.ntt_inplace().expect("NTT failed");
group.bench_function("generic_3329_pointwise", |b| {
b.iter(|| {
let result = poly_a.ntt_mul(&poly_b);
black_box(result)
})
});
}
group.finish();
}
pub fn bench_full_polynomial_multiplication(c: &mut Criterion) {
let mut group = c.benchmark_group("full_polynomial_multiplication");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
{
let poly_a = <DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let poly_b = <DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("ml_dsa_ntt_based", |b| {
b.iter_batched(
|| (poly_a.clone(), poly_b.clone()),
|(mut a, mut b)| {
a.ntt_inplace().expect("NTT failed");
b.ntt_inplace().expect("NTT failed");
let mut result = a.ntt_mul(&b);
result.from_ntt_inplace().expect("Inverse NTT failed");
black_box(result)
},
criterion::BatchSize::SmallInput,
)
});
group.bench_function("ml_dsa_schoolbook", |b| {
b.iter_batched(
|| (poly_a.clone(), poly_b.clone()),
|(a, b)| {
let result = a.schoolbook_mul(&b);
black_box(result)
},
criterion::BatchSize::SmallInput,
)
});
}
{
let poly_a =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let poly_b =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("generic_3329_ntt_based", |b| {
b.iter_batched(
|| (poly_a.clone(), poly_b.clone()),
|(mut a, mut b)| {
a.ntt_inplace().expect("NTT failed");
b.ntt_inplace().expect("NTT failed");
let mut result = a.ntt_mul(&b);
result.from_ntt_inplace().expect("Inverse NTT failed");
black_box(result)
},
criterion::BatchSize::SmallInput,
)
});
group.bench_function("generic_3329_schoolbook", |b| {
b.iter_batched(
|| (poly_a.clone(), poly_b.clone()),
|(a, b)| {
let result = a.schoolbook_mul(&b);
black_box(result)
},
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
}
pub fn bench_montgomery_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("montgomery_operations");
group.bench_function("ml_dsa_montgomery_reduce", |b| {
let a: u64 = 0x12345678_9ABCDEF0;
b.iter(|| {
let result = montgomery_reduce::<MlDsaParams>(black_box(a));
black_box(result)
})
});
group.bench_function("generic_3329_montgomery_reduce", |b| {
let a: u64 = 0x12345678;
b.iter(|| {
let result = montgomery_reduce::<Benchmark3329Params>(black_box(a));
black_box(result)
})
});
group.finish();
}
pub fn bench_ntt_scaling(c: &mut Criterion) {
let mut group = c.benchmark_group("ntt_scaling");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
let sizes = vec![("n256", 256)];
for (label, _size) in sizes {
let poly = <DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_with_input(BenchmarkId::new("ml_dsa", label), &poly, |b, p| {
b.iter_batched(
|| p.clone(),
|mut poly| {
poly.ntt_inplace().expect("NTT failed");
black_box(poly)
},
criterion::BatchSize::SmallInput,
)
});
let poly =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_with_input(BenchmarkId::new("generic_3329", label), &poly, |b, p| {
b.iter_batched(
|| p.clone(),
|mut poly| {
poly.ntt_inplace().expect("NTT failed");
black_box(poly)
},
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
}
pub fn bench_ntt_roundtrip(c: &mut Criterion) {
let mut group = c.benchmark_group("ntt_roundtrip");
let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
let poly_ml_dsa =
<DefaultSamplers as UniformSampler<MlDsaParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("ml_dsa", |b| {
b.iter_batched(
|| poly_ml_dsa.clone(),
|mut p| {
p.ntt_inplace().expect("NTT failed");
p.from_ntt_inplace().expect("Inverse NTT failed");
black_box(p)
},
criterion::BatchSize::SmallInput,
)
});
let poly_generic_3329 =
<DefaultSamplers as UniformSampler<Benchmark3329Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("generic_3329", |b| {
b.iter_batched(
|| poly_generic_3329.clone(),
|mut p| {
p.ntt_inplace().expect("NTT failed");
p.from_ntt_inplace().expect("Inverse NTT failed");
black_box(p)
},
criterion::BatchSize::SmallInput,
)
});
group.finish();
}
}
#[cfg(feature = "alloc")]
fn run_ntt_benchmarks(c: &mut Criterion) {
ntt_benchmarks::bench_ml_dsa_forward_ntt(c);
ntt_benchmarks::bench_ml_dsa_inverse_ntt(c);
ntt_benchmarks::bench_generic_3329_forward_ntt(c);
ntt_benchmarks::bench_generic_3329_inverse_ntt(c);
ntt_benchmarks::bench_ntt_multiplication(c);
ntt_benchmarks::bench_full_polynomial_multiplication(c);
ntt_benchmarks::bench_montgomery_operations(c);
ntt_benchmarks::bench_ntt_scaling(c);
ntt_benchmarks::bench_ntt_roundtrip(c);
}
#[cfg(not(feature = "alloc"))]
fn run_ntt_benchmarks(_c: &mut Criterion) {
eprintln!("NTT benchmarks require the 'alloc' feature. Run with: cargo bench --bench ntt --features alloc");
}
criterion_group!(benches, run_ntt_benchmarks);
criterion_main!(benches);