diff --git a/Cargo.lock b/Cargo.lock index afbd617..9e3f053 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,24 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "hcn" -version = "1.0.0" +version = "1.0.0-multithread" +dependencies = [ + "itertools", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] diff --git a/Cargo.toml b/Cargo.toml index 99d8300..59907bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "hcn" -version = "1.0.0" +version = "1.0.0-multithread" edition = "2021" [dependencies] +itertools = "0.13" [lints.rust] unsafe_code = "forbid" diff --git a/src/main.rs b/src/main.rs index 7a97081..e37d2e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,13 @@ mod sieve; -use std::time::Instant; +use std::{ + collections::HashMap, + env, + thread::{self, available_parallelism}, + time::Instant, +}; +use itertools::Itertools as _; use sieve::get_primes; const MAX_SIEVED_PRIMES: usize = 100_000_000; @@ -44,20 +50,71 @@ fn prime_factors(nr: u64, primes: &[u64]) -> u64 { num_teilers } +fn the_thread(mut start: u64, end: u64, primes: &[u64]) -> HashMap { + let mut max_teilers = 0; + let mut max_teilers_hashmap = HashMap::new(); + + let mut step_size = 10; + + if start == 0 { + start = 2; + step_size = 2; + } + + for nr in (start..=end).step_by(step_size) { + let teilers = prime_factors(nr, primes); + if teilers > max_teilers { + max_teilers = teilers; + max_teilers_hashmap.insert(nr, teilers); + } + } + + max_teilers_hashmap +} + +fn calculate_chunk_bounds(i: usize, num_threads: usize, max_nr: usize) -> (usize, usize) { + let chunk_size = max_nr / num_threads; + let start = i * chunk_size; + let end = ((i + 1) * chunk_size).min(max_nr); + println!("Thread {i}: {start} - {end}"); + (start, end) +} + fn main() { + let max_nr = env::args() + .nth(1) + .expect("Usage: hcn ") + .parse::() + .expect("Invalid max_nr"); + let start = Instant::now(); println!("Precalculating primes..."); let primes = get_primes(MAX_SIEVED_PRIMES); println!("{} primes. Took {:?}", primes.len(), start.elapsed()); + #[allow(clippy::unwrap_used)] + let num_threads = available_parallelism().unwrap().get(); + let mut threads = Vec::with_capacity(num_threads); + + for i in 0..num_threads { + let (start, end) = calculate_chunk_bounds(i, num_threads, max_nr); + let primes = primes.clone(); + threads.push(thread::spawn(move || { + the_thread(start as u64, end as u64, &primes) + })); + } + + let mut results = HashMap::new(); + for thread in threads { + let thread_result = thread.join().expect("Thread failed to join (panicked?)"); + results.extend(thread_result); + } + let mut max_teilers = 0; - let start = Instant::now(); - - for nr in (2..1_000_000_000).step_by(2) { - let teilers = prime_factors(nr, &primes); + for (nr, teilers) in results.into_iter().sorted() { if teilers > max_teilers { - println!("{nr}: {teilers} ({:?} since start)", start.elapsed()); + println!("{nr}: {teilers}"); max_teilers = teilers; } }