use criterion::{criterion_group, criterion_main, Criterion};
#[cfg(feature = "alloc")]
mod ntt_benchmarks {
use criterion::{black_box, BenchmarkId, Criterion};
use dcrypt_algorithms::poly::params::{DilithiumParams, Kyber256Params};
use dcrypt_algorithms::poly::prelude::*;
use dcrypt_algorithms::poly::sampling::{DefaultSamplers, UniformSampler};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
pub fn bench_dilithium_forward_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("dilithium_ntt");
let mut rng = ChaCha20Rng::seed_from_u64(42);
let poly = <DefaultSamplers as UniformSampler<DilithiumParams>>::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_dilithium_inverse_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("dilithium_ntt");
let mut rng = ChaCha20Rng::seed_from_u64(42);
let mut poly =
<DefaultSamplers as UniformSampler<DilithiumParams>>::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_kyber_forward_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("kyber_ntt");
let mut rng = ChaCha20Rng::seed_from_u64(42);
let poly = <DefaultSamplers as UniformSampler<Kyber256Params>>::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_kyber_inverse_ntt(c: &mut Criterion) {
let mut group = c.benchmark_group("kyber_ntt");
let mut rng = ChaCha20Rng::seed_from_u64(42);
let mut poly =
<DefaultSamplers as UniformSampler<Kyber256Params>>::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::seed_from_u64(42);
{
let mut poly_a =
<DefaultSamplers as UniformSampler<DilithiumParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let mut poly_b =
<DefaultSamplers as UniformSampler<DilithiumParams>>::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("dilithium_pointwise", |b| {
b.iter(|| {
let result = poly_a.ntt_mul(&poly_b);
black_box(result)
})
});
}
{
let mut poly_a =
<DefaultSamplers as UniformSampler<Kyber256Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let mut poly_b =
<DefaultSamplers as UniformSampler<Kyber256Params>>::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("kyber_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::seed_from_u64(42);
{
let poly_a =
<DefaultSamplers as UniformSampler<DilithiumParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let poly_b =
<DefaultSamplers as UniformSampler<DilithiumParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("dilithium_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("dilithium_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<Kyber256Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
let poly_b =
<DefaultSamplers as UniformSampler<Kyber256Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("kyber_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("kyber_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("dilithium_montgomery_reduce", |b| {
let a: u64 = 0x12345678_9ABCDEF0;
b.iter(|| {
let result = montgomery_reduce::<DilithiumParams>(black_box(a));
black_box(result)
})
});
group.bench_function("kyber_montgomery_reduce", |b| {
let a: u64 = 0x12345678;
b.iter(|| {
let result = montgomery_reduce::<Kyber256Params>(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::seed_from_u64(42);
let sizes = vec![("n256", 256)];
for (label, _size) in sizes {
let poly =
<DefaultSamplers as UniformSampler<DilithiumParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_with_input(BenchmarkId::new("dilithium", 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<Kyber256Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_with_input(BenchmarkId::new("kyber", 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::seed_from_u64(42);
let poly_dilithium =
<DefaultSamplers as UniformSampler<DilithiumParams>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("dilithium", |b| {
b.iter_batched(
|| poly_dilithium.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_kyber =
<DefaultSamplers as UniformSampler<Kyber256Params>>::sample_uniform(&mut rng)
.expect("Failed to sample polynomial");
group.bench_function("kyber", |b| {
b.iter_batched(
|| poly_kyber.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_dilithium_forward_ntt(c);
ntt_benchmarks::bench_dilithium_inverse_ntt(c);
ntt_benchmarks::bench_kyber_forward_ntt(c);
ntt_benchmarks::bench_kyber_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);