big performance uplift

This commit is contained in:
code002lover 2024-07-17 00:56:13 +02:00
parent 099012a60b
commit ce507071b9
6 changed files with 197 additions and 119 deletions

6
.gitignore vendored
View File

@ -1,6 +1,6 @@
/target /target
report.json report.json
primes.json primes.json
primes1.json primes*.json
primes1.txt primes*.txt
primes1.zip primes*.zip

80
Cargo.lock generated
View File

@ -39,9 +39,16 @@ dependencies = [
"num-bigint", "num-bigint",
"once_cell", "once_cell",
"rand", "rand",
"serde_json",
"thread-priority", "thread-priority",
] ]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.155" version = "0.2.155"
@ -76,6 +83,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [ dependencies = [
"num-integer", "num-integer",
"num-traits", "num-traits",
"serde",
] ]
[[package]] [[package]]
@ -139,6 +147,24 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 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]] [[package]]
name = "rand" name = "rand"
version = "0.8.5" version = "0.8.5"
@ -175,6 +201,54 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 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]] [[package]]
name = "thread-priority" name = "thread-priority"
version = "1.1.0" version = "1.1.0"
@ -189,6 +263,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View File

@ -9,8 +9,9 @@ edition = "2021"
num = "0.4" num = "0.4"
thread-priority = "1.1" thread-priority = "1.1"
rand = "0.8" 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" once_cell = "1.19"
serde_json = "1.0"
[profile.release] [profile.release]
lto = true # Enable link-time optimization lto = true # Enable link-time optimization
@ -21,3 +22,21 @@ overflow-checks = false
[profile.dev] [profile.dev]
opt-level = 3 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"

View File

@ -1,7 +1,5 @@
pub mod prime_utils { pub mod prime_utils {
use num::{BigUint, One, Zero}; use num::{BigUint, One};
use crate::primality_test::primality_tests::is_probably_prime;
#[must_use] #[must_use]
pub fn log_2(x: &BigUint) -> u64 { pub fn log_2(x: &BigUint) -> u64 {
@ -17,42 +15,27 @@ pub mod prime_utils {
return true; 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 // number = 2^a - 1
// a = log2(number + 1) // a = log2(number + 1)
let a = log_2(&(number + 1u8)); let a = log_2(&(number + 1u8));
//if number isn't a mersenne number
if BigUint::from(2u8).pow(a as u32) - BigUint::one() != *number { if BigUint::from(2u8).pow(a as u32) - BigUint::one() != *number {
const ZERO: BigUint = BigUint::ZERO;
let mut i = BigUint::one(); let mut i = BigUint::one();
let one = 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 { for prime in g_primes {
if prime < &sqrtnum && number % prime == zero { if prime < &sqrtnum && number % prime == ZERO {
if number == &BigUint::from(23u8) {
eprintln!("Prime table check failed");
}
return false; return false;
} }
} }
loop { loop {
i += &one; i += &one;
if number % &i == zero { if number % &i == ZERO {
if number == &BigUint::from(23u8) {
eprintln!("manual check failed");
}
return false; return false;
} }
if i == sqrtnum { if i == sqrtnum {
@ -61,19 +44,15 @@ pub mod prime_utils {
} }
} }
//check if it's a mersenne prime
// 4 12 194 // 4 12 194
let mut last = BigUint::from(4u8); let mut last = BigUint::from(4u8);
let two = BigUint::from(2u8);
for _i in 2..a { for _i in 2..a {
last = (last.pow(2) - &two) % number; last = (last.pow(2) - &two) % number;
} }
let ret = last == BigUint::from(0u8); last == BigUint::from(0u8)
if number == &BigUint::from(23u8) && !ret {
eprintln!("RET check failed");
}
ret
} }
} }

View File

@ -9,7 +9,10 @@ use num::{BigUint, One};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::{ use std::{
io::Write, io::Write,
sync::atomic::{AtomicI64, AtomicUsize}, sync::{
atomic::{AtomicU64, AtomicUsize},
Arc, RwLock,
},
thread, thread,
time::SystemTime, time::SystemTime,
}; };
@ -40,8 +43,8 @@ fn to_f64(u: usize) -> f64 {
f64::from_bits(u as u64) f64::from_bits(u as u64)
} }
#[cfg(target_os = "windows")]
fn try_set_thread_priority() { fn try_set_thread_priority() {
#[cfg(target_os = "windows")]
if let Err(e) = set_current_thread_priority(ThreadPriority::Max) { if let Err(e) = set_current_thread_priority(ThreadPriority::Max) {
eprintln!("[WARN] failed to set thread priority to max"); eprintln!("[WARN] failed to set thread priority to max");
eprintln!("{e}"); eprintln!("{e}");
@ -50,26 +53,78 @@ fn try_set_thread_priority() {
fn write_primes(primes: &[BigUint]) { fn write_primes(primes: &[BigUint]) {
let mut file = std::fs::File::create("primes.json").unwrap(); let mut file = std::fs::File::create("primes.json").unwrap();
file.write_all(b"[").unwrap(); file.write_all(serde_json::to_string(primes).unwrap().as_bytes())
let mut it = primes.iter().peekable(); .unwrap();
while let Some(prime) = it.next() { }
file.write_all(prime.to_string().as_bytes()).unwrap();
if it.peek().is_some() { const N: i64 = 1_000_000;
file.write_all(b",").unwrap(); 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<RwLock<Vec<BigUint>>>,
threads_done: &AtomicUsize,
) {
#[cfg(target_os = "windows")]
try_set_thread_priority();
static INC: once_cell::sync::Lazy<BigUint> = 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() { 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 sys_time = SystemTime::now();
let nthprime_approx: usize = to_usize(p(NF * 1.02)); let nthprime_approx: usize = to_usize(p(NF * 1.02));
@ -77,10 +132,10 @@ fn main() {
let workloads = nthprime_approx / THREADS; let workloads = nthprime_approx / THREADS;
let primecache: Vec<BigUint> = Vec::with_capacity(NU); let primecache: Vec<BigUint> = 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 primearc = std::sync::Arc::new(primemutex);
let progress_val = AtomicI64::new(0); let progress_val = AtomicU64::new(0);
let progress = &progress_val; let progress = &progress_val;
let threads_done_val = AtomicUsize::new(0); let threads_done_val = AtomicUsize::new(0);
@ -89,56 +144,7 @@ fn main() {
thread::scope(|scope| { thread::scope(|scope| {
for i in 0..THREADS { for i in 0..THREADS {
let arcclone = primearc.clone(); let arcclone = primearc.clone();
scope.spawn(move || { scope.spawn(move || worker(workloads, i, progress, &arcclone, threads_done));
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<BigUint> = 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);
});
} }
loop { 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 removed: i32 = if primes.len() > NU {
let mut rem = 0; let rem = primes.len() - NU;
loop { primes.truncate(NU);
if primes.len() <= NU { rem as i32
break;
}
rem += 1;
primes.pop();
}
rem
} else { } else {
0 0
}; };
let added = NU - primes.len(); let added = NU - primes.len();
if primes.len() < NU { if primes.len() < NU {
static INC: once_cell::sync::Lazy<BigUint> = Lazy::new(BigUint::one);
println!("[INFO] less primes than expected"); println!("[INFO] less primes than expected");
let mut number = BigUint::from(THREADS) * BigUint::from(workloads); let mut number = BigUint::from(THREADS) * BigUint::from(workloads);
static INC: once_cell::sync::Lazy<BigUint> = Lazy::new(BigUint::one);
loop { loop {
number += &*INC; number += &*INC;
if prime_utils::is_prime(&number, &primes) { if prime_utils::is_prime(&number, &primes) {
@ -192,10 +194,7 @@ fn main() {
} }
} }
let new_sys_time = SystemTime::now(); let difference = sys_time.elapsed().unwrap();
let difference = new_sys_time
.duration_since(sys_time)
.expect("Clock may have gone backwards");
assert_eq!(primes.len(), NU); assert_eq!(primes.len(), NU);

View File

@ -10,6 +10,7 @@ pub mod primality_tests {
BigUint::from_bytes_be(&buf) BigUint::from_bytes_be(&buf)
} }
#[must_use]
pub fn is_probably_prime(number: &BigUint, iterations: u32) -> bool { pub fn is_probably_prime(number: &BigUint, iterations: u32) -> bool {
if number <= &BigUint::one() || number == &BigUint::from(4u32) { if number <= &BigUint::one() || number == &BigUint::from(4u32) {
return false; return false;