elements_frequency/
interface.rs

1use num_cpus;
2use rayon_core::scope;
3use std::collections::HashMap;
4use std::fmt::Debug;
5use std::hash::{BuildHasherDefault, Hash};
6use std::sync::Mutex;
7use twox_hash::XxHash64;
8
9#[allow(clippy::drop_copy)]
10/// Examples
11/// ```
12/// use elements_frequency::interface::frequency_finder;
13///
14/// let myList = [1, 1, -6, 2, 6, 2, 7, 1];
15///
16/// let frequency_map = frequency_finder(&myList);
17/// ```
18pub fn frequency_finder<T>(list: &[T]) -> HashMap<T, u64, BuildHasherDefault<XxHash64>>
19where
20	T: Copy + Hash + Eq + Sync + Send + Debug,
21{
22	// Number of threads available on this system
23	let logical_cores = num_cpus::get();
24	// Define How many elements each thread will handle
25	let range: usize = list.len() / logical_cores;
26	// If number of elements are not divisible by number of threads,
27	// -> after this index all elements will be processed in a separate thread
28	let split_index: usize = logical_cores * range;
29	// Number of elements to be processed in a separate thread
30	let remainder: usize = list.len() % logical_cores;
31
32	// A map that hashes every unique elements to it's frequency
33	let map_mtx: Mutex<HashMap<T, u64, BuildHasherDefault<XxHash64>>> =
34		Mutex::new(Default::default());
35	// Can't pass `map_mtx` into closure as the value will move, so pass a ref instead
36	let map_ref = &map_mtx;
37
38	scope(|s| {
39		for idx in 1..=logical_cores {
40			s.spawn(move |_| {
41				let mut map_guard = map_ref.lock().unwrap();
42
43				for item in list.iter().take(idx * range).skip((idx - 1) * range) {
44					match map_guard.get_mut(item) {
45						// If the item(key) is found, increment it's count(value) by 1
46						Some(val) => *val += 1,
47						// Otherwise insert the item and set the count to 1, and drop return value
48						None => drop(map_guard.insert(*item, 1)),
49					}
50				}
51			});
52		}
53
54		// One separate thread for rest of the elements
55		s.spawn(move |_| {
56			let mut map_guard = map_ref.lock().unwrap();
57
58			for item in list.iter().skip(split_index).take(remainder) {
59				match map_guard.get_mut(item) {
60					Some(val) => *val += 1,
61
62					None => drop(map_guard.insert(*item, 1)),
63				}
64			}
65		})
66	});
67
68	Mutex::into_inner(map_mtx).unwrap()
69}