From 46ea4af775417534ede8aec04f4ac2d073225b68 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 18 Apr 2022 10:30:23 -0400 Subject: [PATCH] Add more benchmarks --- bench/Cargo.toml | 1 + bench/benches/bench.rs | 153 ++++++++++++++++++++++++++++++++++++++++- src/nt_funcs.rs | 26 +++++-- src/traits.rs | 3 + 4 files changed, 176 insertions(+), 7 deletions(-) diff --git a/bench/Cargo.toml b/bench/Cargo.toml index a19ff24..0c8ab6f 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -18,6 +18,7 @@ num-primes = { version = "0.3.0", optional = true } primal-check = "0.3.1" number-theory = "0.0.6" is_prime = "2.0.7" +glass_pumpkin = "1.2.0" [features] default = ["num-primes"] diff --git a/bench/benches/bench.rs b/bench/benches/bench.rs index e34ca56..095f7d9 100644 --- a/bench/benches/bench.rs +++ b/bench/benches/bench.rs @@ -1,11 +1,15 @@ #[macro_use] extern crate criterion; +use std::iter::repeat_with; + use criterion::{Criterion, SamplingMode}; +use num_bigint::RandBigInt; use num_prime::{nt_funcs, RandPrime}; #[cfg(feature = "num-primes")] use num_primes::{Generator, Verification}; use primal_check::miller_rabin; use number_theory::NumberTheory; +use glass_pumpkin::{prime as gprime, safe_prime as safe_gprime}; pub fn bench_is_prime(c: &mut Criterion) { const N0: u64 = 1_000_000; @@ -17,7 +21,7 @@ pub fn bench_is_prime(c: &mut Criterion) { .step_by(STEP) .chain((N1..N2).step_by(STEP)); - let mut group = c.benchmark_group("is_prime (u64)"); + let mut group = c.benchmark_group("primality check (u64)"); group.bench_function("num-prime (this crate)", |b| { b.iter(|| { @@ -34,6 +38,13 @@ pub fn bench_is_prime(c: &mut Criterion) { .count() }) }); + group.bench_function("glass_pumpkin", |b| { + b.iter(|| { + numbers() + .filter(|&n| gprime::check(&n.into())) + .count() + }) + }); group.bench_function("primal-check", |b| { b.iter(|| { numbers() @@ -52,6 +63,140 @@ pub fn bench_is_prime(c: &mut Criterion) { // we can add a markdown of performance after figuring out this group.finish(); + + ////// 256 bits Bigint ///// + + let mut rng = rand::thread_rng(); + let numbers: Vec<_> = repeat_with(|| rng.gen_biguint(256)).take(32).collect(); + + let mut group = c.benchmark_group("primality check (u256)"); + group.bench_function("num-prime (this crate)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| nt_funcs::is_prime(n, None).probably()) + .count() + }) + }); + group.bench_function("num-primes", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| Verification::is_prime(&num_primes::BigUint::from_bytes_le(&n.to_bytes_le()))) + .count() + }) + }); + group.bench_function("glass_pumpkin", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| gprime::check(n)) + .count() + }) + }); + group.bench_function("glass_pumpkin (BPSW)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| gprime::strong_check(n)) + .count() + }) + }); + group.finish(); + + let mut group = c.benchmark_group("safe primality check (u256)"); + group.bench_function("num-prime (this crate)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| nt_funcs::is_safe_prime(n).probably()) + .count() + }) + }); + group.bench_function("num-primes", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| Verification::is_safe_prime(&num_primes::BigUint::from_bytes_le(&n.to_bytes_le()))) + .count() + }) + }); + group.bench_function("glass_pumpkin", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| safe_gprime::check(n)) + .count() + }) + }); + group.bench_function("glass_pumpkin (BPSW)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| safe_gprime::strong_check(n)) + .count() + }) + }); + group.finish(); + + ////// 2048 bits Bigint ///// + + let mut rng = rand::thread_rng(); + let numbers: Vec<_> = repeat_with(|| rng.gen_biguint(2048)).take(8).collect(); + + let mut group = c.benchmark_group("primality check (u2048)"); + group.bench_function("num-prime (this crate)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| nt_funcs::is_prime(n, None).probably()) + .count() + }) + }); + group.bench_function("num-primes", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| Verification::is_prime(&num_primes::BigUint::from_bytes_le(&n.to_bytes_le()))) + .count() + }) + }); + group.bench_function("glass_pumpkin", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| gprime::check(n)) + .count() + }) + }); + group.bench_function("glass_pumpkin (BPSW)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| gprime::strong_check(n)) + .count() + }) + }); + group.finish(); + + let mut group = c.benchmark_group("safe primality check (u2048)"); + group.bench_function("num-prime (this crate)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| nt_funcs::is_safe_prime(n).probably()) + .count() + }) + }); + group.bench_function("num-primes", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| Verification::is_safe_prime(&num_primes::BigUint::from_bytes_le(&n.to_bytes_le()))) + .count() + }) + }); + group.bench_function("glass_pumpkin", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| safe_gprime::check(n)) + .count() + }) + }); + group.bench_function("glass_pumpkin (BPSW)", |b| { + b.iter(|| { + numbers.iter() + .filter(|&n| safe_gprime::strong_check(n)) + .count() + }) + }); + group.finish(); } pub fn bench_factorization(c: &mut Criterion) { @@ -94,6 +239,9 @@ pub fn bench_prime_gen(c: &mut Criterion) { group.bench_function("num-primes", |b| { b.iter(|| Generator::new_prime(256)) }); + group.bench_function("glass_pumpkin", |b| { + b.iter(|| gprime::from_rng(256, &mut gen)) + }); group.finish(); let mut group = c.benchmark_group("safe prime generation (256 bits)"); @@ -106,6 +254,9 @@ pub fn bench_prime_gen(c: &mut Criterion) { group.bench_function("num-primes", |b| { b.iter(|| Generator::safe_prime(256)) }); + group.bench_function("glass_pumpkin", |b| { + b.iter(|| safe_gprime::from_rng(256, &mut gen)) + }); group.finish(); } diff --git a/src/nt_funcs.rs b/src/nt_funcs.rs index 6986e4c..bb745f2 100644 --- a/src/nt_funcs.rs +++ b/src/nt_funcs.rs @@ -553,6 +553,8 @@ where { let buf = NaiveBuffer::new(); let config = Some(PrimalityTestConfig::strict()); + // TODO: use miller-rabin for large numbers (more than 256 bits?), as BPSW could be too slow + // the NIST recommends 5 rounds for 512 and 1024 bits. For 1536 bits, the recommendation is 4 rounds. // test (n-1)/2 first since its smaller let sophie_p = buf.is_prime(&(target >> 1), config); @@ -712,7 +714,7 @@ macro_rules! impl_randprime_prim { panic!("The given bit size limit exceeded the capacity of the integer type!") } let t: $T = self.gen(); - let t = t >> (<$T>::BITS - bit_size as u32); + let t = (t >> (<$T>::BITS - bit_size as u32)) | 1; // filter even numbers if is_prime64(t as u64) { t } else { @@ -755,7 +757,7 @@ impl RandPrime for R { panic!("The given bit size limit exceeded the capacity of the integer type!") } let t: u128 = self.gen(); - let t = t >> (u128::BITS - bit_size as u32); + let t = (t >> (u128::BITS - bit_size as u32)) | 1; // filter even numbers if is_prime(&t, config).probably() { t } else { @@ -783,11 +785,11 @@ impl RandPrime for R { } } -#[cfg(feature = "big-int")] +#[cfg(feature = "num-bigint")] impl RandPrime for R { #[inline] fn gen_prime(&mut self, bit_size: usize, config: Option) -> BigUint { - let t = self.gen_biguint(bit_size as u64); + let t = self.gen_biguint(bit_size as u64) | BigUint::from(1u8); // filter even numbers if is_prime(&t, config).probably() { t } else { @@ -967,6 +969,18 @@ mod tests { } } + #[test] + #[cfg(feature = "big-int")] + fn is_prime_test() { + #[cfg(feature = "num-bigint")] + { + use num_bigint::BigUint; + // https://github.com/AtropineTears/num-primes/issues/1#issuecomment-934629597 + let p = BigUint::parse_bytes(b"169511182982703321453314585423962898651587669459838234386506572286328885534468792292646838949809616446341407457141008401355628947670484184607678853094537849610289912805960069455687743151708433319901176932959509872662610091644590437761688516626993416011399330087939042347256922771590903190536793274742859624657", 10).unwrap(); + assert!(is_prime(&p, None).probably()); + } + } + #[test] fn is_safe_prime_test() { // OEIS:A005385 @@ -1181,12 +1195,12 @@ mod tests { let p: u128 = rng.gen_safe_prime(128); assert!(is_safe_prime(&p).probably()); - #[cfg(feature = "big-int")] + #[cfg(feature = "num-bigint")] { let p: BigUint = rng.gen_prime(512, None); assert!(is_prime(&p, None).probably()); let p: BigUint = rng.gen_safe_prime(192); - assert!(is_prime(&p, None).probably()); + assert!(is_safe_prime(&p).probably()); } // test bit size limit diff --git a/src/traits.rs b/src/traits.rs index 789cb59..e1f3382 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -33,6 +33,7 @@ pub enum Primality { impl Primality { /// Check whether the resule indicates that the number is /// (very) probably a prime. Return false only on [Primality::No] + #[inline(always)] pub fn probably(self) -> bool { match self { Primality::No => false, @@ -276,4 +277,6 @@ pub trait RandPrime { /// Generate a random (Sophie German) safe prime within the given bit size limit. The generated prime /// is guaranteed to pass the [is_safe_prime][crate::nt_funcs::is_safe_prime] test fn gen_safe_prime(&mut self, bit_size: usize) -> T; + + // TODO: add gen_prime_exact and gen_safe_prime_exact for generating primes with exact this bit size }