diff --git a/.gitignore b/.gitignore index afead34..a5ba478 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /target report.json primes.json -primes1.json -primes1.txt -primes1.zip +primes*.json +primes*.txt +primes*.zip diff --git a/Cargo.lock b/Cargo.lock index e16ef91..fd94ea1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,9 +39,16 @@ dependencies = [ "num-bigint", "once_cell", "rand", + "serde_json", "thread-priority", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "libc" version = "0.2.155" @@ -76,6 +83,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "serde", ] [[package]] @@ -139,6 +147,24 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.8.5" @@ -175,6 +201,54 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "thread-priority" version = "1.1.0" @@ -189,6 +263,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index fec6a5d..6b06676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,9 @@ edition = "2021" num = "0.4" thread-priority = "1.1" rand = "0.8" -num-bigint = { default-features = false, version = "0.4" } +num-bigint = { default-features = false, version = "0.4", features = ["serde"] } once_cell = "1.19" +serde_json = "1.0" [profile.release] lto = true # Enable link-time optimization @@ -21,3 +22,21 @@ overflow-checks = false [profile.dev] opt-level = 3 + +[lints.clippy] +pedantic = { level = "deny", priority = -1 } +perf = { level = "deny", priority = 1 } +complexity = { level = "deny", priority = -1 } +style = { level = "deny", priority = -1 } +correctness = { level = "deny", priority = -1 } +format_collect = "deny" +format_in_format_args = "deny" +format_push_string = "deny" +mut_mut = "deny" +let_underscore_untyped = "deny" +str_to_string = "deny" + +cast_possible_truncation = "allow" +cast_precision_loss = "allow" +cast_sign_loss = "allow" +missing_errors_doc = "allow" \ No newline at end of file diff --git a/src/is_prime.rs b/src/is_prime.rs index 9043ac2..6407f7a 100644 --- a/src/is_prime.rs +++ b/src/is_prime.rs @@ -1,7 +1,5 @@ pub mod prime_utils { - use num::{BigUint, One, Zero}; - - use crate::primality_test::primality_tests::is_probably_prime; + use num::{BigUint, One}; #[must_use] pub fn log_2(x: &BigUint) -> u64 { @@ -17,42 +15,27 @@ pub mod prime_utils { return true; } - let sqrtnum = number.sqrt(); - - if sqrtnum.pow(2) == *number { - if number == &BigUint::from(23u8) { - eprintln!("SQRT check failed"); - } - return false; - } - - let two = BigUint::from(2u8); - // number = 2^a - 1 // a = log2(number + 1) let a = log_2(&(number + 1u8)); + + //if number isn't a mersenne number if BigUint::from(2u8).pow(a as u32) - BigUint::one() != *number { + const ZERO: BigUint = BigUint::ZERO; let mut i = BigUint::one(); let one = BigUint::one(); - let zero = BigUint::zero(); - let sqrtnum = sqrtnum + &one; //fake ceil function + let sqrtnum = number.sqrt() + &one; //fake ceil function for prime in g_primes { - if prime < &sqrtnum && number % prime == zero { - if number == &BigUint::from(23u8) { - eprintln!("Prime table check failed"); - } + if prime < &sqrtnum && number % prime == ZERO { return false; } } loop { i += &one; - if number % &i == zero { - if number == &BigUint::from(23u8) { - eprintln!("manual check failed"); - } + if number % &i == ZERO { return false; } if i == sqrtnum { @@ -61,19 +44,15 @@ pub mod prime_utils { } } + //check if it's a mersenne prime // 4 12 194 let mut last = BigUint::from(4u8); + let two = BigUint::from(2u8); for _i in 2..a { last = (last.pow(2) - &two) % number; } - let ret = last == BigUint::from(0u8); - - if number == &BigUint::from(23u8) && !ret { - eprintln!("RET check failed"); - } - - ret + last == BigUint::from(0u8) } } diff --git a/src/main.rs b/src/main.rs index e4f30bf..0b1ced5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,10 @@ use num::{BigUint, One}; use once_cell::sync::Lazy; use std::{ io::Write, - sync::atomic::{AtomicI64, AtomicUsize}, + sync::{ + atomic::{AtomicU64, AtomicUsize}, + Arc, RwLock, + }, thread, time::SystemTime, }; @@ -40,8 +43,8 @@ fn to_f64(u: usize) -> f64 { f64::from_bits(u as u64) } +#[cfg(target_os = "windows")] fn try_set_thread_priority() { - #[cfg(target_os = "windows")] if let Err(e) = set_current_thread_priority(ThreadPriority::Max) { eprintln!("[WARN] failed to set thread priority to max"); eprintln!("{e}"); @@ -50,26 +53,78 @@ fn try_set_thread_priority() { fn write_primes(primes: &[BigUint]) { let mut file = std::fs::File::create("primes.json").unwrap(); - file.write_all(b"[").unwrap(); - let mut it = primes.iter().peekable(); - while let Some(prime) = it.next() { - file.write_all(prime.to_string().as_bytes()).unwrap(); - if it.peek().is_some() { - file.write_all(b",").unwrap(); + file.write_all(serde_json::to_string(primes).unwrap().as_bytes()) + .unwrap(); +} + +const N: i64 = 1_000_000; +const NF: f64 = N as f64; +const NU: usize = N as usize; +const THREADS: usize = 30; +const SPLITTER: u64 = N as u64 / THREADS as u64; + +fn worker( + workloads: usize, + i: usize, + progress: &AtomicU64, + arcclone: &Arc>>, + threads_done: &AtomicUsize, +) { + #[cfg(target_os = "windows")] + try_set_thread_priority(); + + static INC: once_cell::sync::Lazy = Lazy::new(BigUint::one); + + let one_thousand = &BigUint::from(SPLITTER * 10); + let one_hundred = &BigUint::from(SPLITTER); + let ten = &BigUint::from(10u8); + + let work_uint = BigUint::from(workloads); + let mut number = BigUint::from(i) * &work_uint; + + let max = BigUint::from(i + 1) * work_uint; + let mut primecache = Vec::with_capacity(NU); + let mut primecache_outside_len = 0; + let mut global_primes = arcclone.read().unwrap(); + loop { + number += &*INC; + + if prime_utils::is_prime(&number, &global_primes) { + primecache.push(number.clone()); + progress.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + if (&number * one_thousand) / &max / ten == &number / &max * one_hundred { + drop(global_primes); + + let mut new_primecache = arcclone.write().unwrap(); + + new_primecache.extend_from_slice(&primecache[primecache_outside_len..]); + new_primecache.sort_unstable(); + + primecache.clone_from(&new_primecache); + primecache_outside_len = new_primecache.len(); + + drop(new_primecache); + + global_primes = arcclone.read().unwrap(); + } + if number == max { + break; } } - file.write_all(b"]").unwrap(); + + drop(global_primes); + + let mut new_primecache = arcclone.write().unwrap(); + + new_primecache.extend_from_slice(&primecache[primecache_outside_len..]); + new_primecache.sort_unstable(); + + threads_done.fetch_add(1, std::sync::atomic::Ordering::Relaxed); } fn main() { - const N: i64 = 1_000_000; - const NF: f64 = N as f64; - const NU: usize = N as usize; - const THREADS: usize = 30; - const SPLITTER: u64 = N as u64 / THREADS as u64; - - try_set_thread_priority(); - let sys_time = SystemTime::now(); let nthprime_approx: usize = to_usize(p(NF * 1.02)); @@ -77,10 +132,10 @@ fn main() { let workloads = nthprime_approx / THREADS; let primecache: Vec = Vec::with_capacity(NU); - let primemutex = std::sync::Mutex::new(primecache); + let primemutex = std::sync::RwLock::new(primecache); let primearc = std::sync::Arc::new(primemutex); - let progress_val = AtomicI64::new(0); + let progress_val = AtomicU64::new(0); let progress = &progress_val; let threads_done_val = AtomicUsize::new(0); @@ -89,56 +144,7 @@ fn main() { thread::scope(|scope| { for i in 0..THREADS { let arcclone = primearc.clone(); - scope.spawn(move || { - try_set_thread_priority(); - - let one_thousand = &BigUint::from(SPLITTER * 10); - let one_hundred = &BigUint::from(SPLITTER); - let ten = &BigUint::from(10u8); - - let work_uint = BigUint::from(workloads); - let mut number = BigUint::from(i) * &work_uint; - static INC: once_cell::sync::Lazy = Lazy::new(BigUint::one); - - let max = BigUint::from(i + 1) * work_uint; - let mut primecache = Vec::with_capacity(NU); - let mut primecache_outside_len = 0; - loop { - number += &*INC; - - if prime_utils::is_prime(&number, &primecache) { - primecache.push(number.clone()); - progress.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - } - if (&number * one_thousand) / &max / ten == &number / &max * one_hundred { - let tried_getting = SystemTime::now(); - let mut new_primecache = arcclone.lock().unwrap(); - if SystemTime::now() - .duration_since(tried_getting) - .unwrap() - .as_millis() - > 50 - { - eprintln!("[WARN] Waited 0.05+ seconds for lock!"); - } - - new_primecache.extend_from_slice(&primecache[primecache_outside_len..]); - new_primecache.sort_unstable(); - - primecache.clone_from(&new_primecache); - primecache_outside_len = new_primecache.len(); - } - if number == max { - break; - } - } - - let mut new_primecache = arcclone.lock().unwrap(); - new_primecache.extend_from_slice(&primecache[primecache_outside_len..]); - new_primecache.sort_unstable(); - - threads_done.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - }); + scope.spawn(move || worker(workloads, i, progress, &arcclone, threads_done)); } loop { @@ -160,27 +166,23 @@ fn main() { } }); - let mut primes = primearc.lock().unwrap().to_vec(); + let mut primes = primearc.write().unwrap(); + #[allow(clippy::cast_possible_wrap)] let removed: i32 = if primes.len() > NU { - let mut rem = 0; - loop { - if primes.len() <= NU { - break; - } - rem += 1; - primes.pop(); - } - rem + let rem = primes.len() - NU; + primes.truncate(NU); + rem as i32 } else { 0 }; let added = NU - primes.len(); if primes.len() < NU { + static INC: once_cell::sync::Lazy = Lazy::new(BigUint::one); + println!("[INFO] less primes than expected"); let mut number = BigUint::from(THREADS) * BigUint::from(workloads); - static INC: once_cell::sync::Lazy = Lazy::new(BigUint::one); loop { number += &*INC; if prime_utils::is_prime(&number, &primes) { @@ -192,10 +194,7 @@ fn main() { } } - let new_sys_time = SystemTime::now(); - let difference = new_sys_time - .duration_since(sys_time) - .expect("Clock may have gone backwards"); + let difference = sys_time.elapsed().unwrap(); assert_eq!(primes.len(), NU); diff --git a/src/primality_test.rs b/src/primality_test.rs index 13df6ee..326f92b 100644 --- a/src/primality_test.rs +++ b/src/primality_test.rs @@ -10,6 +10,7 @@ pub mod primality_tests { BigUint::from_bytes_be(&buf) } + #[must_use] pub fn is_probably_prime(number: &BigUint, iterations: u32) -> bool { if number <= &BigUint::one() || number == &BigUint::from(4u32) { return false;