From: MA Beaudet Date: Mon, 15 Nov 2021 10:09:04 +0000 (+0100) Subject: perf(parallel freq): remove mutexes in favor of fold for counters X-Git-Url: https://git.beaudet.xyz/?a=commitdiff_plain;p=poker-eval.git perf(parallel freq): remove mutexes in favor of fold for counters --- diff --git a/src/lib.rs b/src/lib.rs index 8be319c..10b64c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,6 @@ pub mod evaluator; use std::{ collections::HashMap, convert::{TryFrom, TryInto}, - sync::{Arc, Mutex}, thread, }; @@ -340,42 +339,49 @@ pub fn deck_freq_evaluator(deck: &[u32]) -> [u32; 10] { } /// Concurrent evaluation of all five-card poker hands. -/// -/// This implementation is actually slower in release builds. -/// This is probably due to the Mutex wrap on `freq`. pub fn parallel_deck_freq_evaluator(deck: &[u32]) -> [u32; 10] { + use std::sync::{mpsc, Arc}; + + fn fold_counters(mut acc: [u32; 10], count: [u32; 10]) -> [u32; 10] { + count.iter().enumerate().for_each(|(k, v)| acc[k] += v); + acc + } + let deck = Arc::new(Vec::from(deck)); - let freq: Arc> = Arc::new(Mutex::new([0; 10])); - let thread_vec = (0..48).collect::>(); - let mut thread_handles = vec![]; - - for v in thread_vec.chunks(6) { - let v = v.to_owned(); - let deck = deck.clone(); - let freq = freq.clone(); - thread_handles.push(thread::spawn(move || { - for a in v { - for b in a + 1..49 { - for c in b + 1..50 { - for d in c + 1..51 { - for e in d + 1..52 { - let i = eval_5hand(&[deck[a], deck[b], deck[c], deck[d], deck[e]]); - let j = hand_rank(i); - let mut freq = freq.lock().unwrap(); - freq[j as usize] += 1; + + // tx goes out of scope before folding counters + // Otherwise you would need to explicitly call `drop(tx)` + let rx = { + let (tx, rx) = mpsc::channel(); + + let thread_vec: Vec = (0..48).into_iter().collect(); + for v in thread_vec.chunks(6) { + let v = v.to_owned(); + let deck = deck.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let mut freq = [0; 10]; + for a in v.clone() { + for b in a + 1..49 { + for c in b + 1..50 { + for d in c + 1..51 { + for e in d + 1..52 { + let i = + eval_5hand(&[deck[a], deck[b], deck[c], deck[d], deck[e]]); + let j = hand_rank(i); + freq[j as usize] += 1; + } } } } } - } - })); - } - - for handle in thread_handles { - handle.join().unwrap(); - } - let freq = *freq.lock().unwrap(); - freq + let _ = tx.send(freq); + }); + } + rx + }; + // drop(tx); + rx.iter().fold([0; 10], fold_counters) } /// Concurrent evaluation of all five-card poker hands using rayon crate.