1use rand::{seq::SliceRandom, thread_rng};
2use rayon::prelude::*;
3use std::collections::hash_map::DefaultHasher;
4use std::collections::HashMap;
5use std::hash::{Hash, Hasher};
6
7pub struct HashSort;
9
10impl HashSort {
11 pub fn hash_sort<T: Clone>(lines: &mut [T], get_key: impl Fn(&T) -> &[u8] + Sync) {
14 if lines.len() < 2 {
15 return;
16 }
17
18 let groups = Self::hash_group_lines(lines, &get_key);
20
21 let shuffled_indices = Self::create_shuffled_indices(&groups);
23
24 Self::reorder_by_indices(lines, &shuffled_indices);
26 }
27
28 fn hash_group_lines<T>(lines: &[T], get_key: impl Fn(&T) -> &[u8]) -> Vec<Vec<usize>> {
30 let mut hash_to_indices: HashMap<u64, Vec<usize>> = HashMap::new();
31
32 for (idx, line) in lines.iter().enumerate() {
34 let key = get_key(line);
35 let hash = Self::fast_hash(key);
36 hash_to_indices.entry(hash).or_default().push(idx);
37 }
38
39 hash_to_indices.into_values().collect()
41 }
42
43 #[inline]
45 fn fast_hash(data: &[u8]) -> u64 {
46 let mut hasher = DefaultHasher::new();
48 data.hash(&mut hasher);
49 hasher.finish()
50 }
51
52 fn create_shuffled_indices(groups: &[Vec<usize>]) -> Vec<usize> {
54 let mut rng = thread_rng();
55 let mut result = Vec::with_capacity(groups.iter().map(|g| g.len()).sum());
56
57 let mut group_order: Vec<usize> = (0..groups.len()).collect();
59 group_order.shuffle(&mut rng);
60
61 for &group_idx in &group_order {
63 result.extend_from_slice(&groups[group_idx]);
64 }
65
66 result
67 }
68
69 fn reorder_by_indices<T: Clone>(lines: &mut [T], indices: &[usize]) {
71 let original = lines.to_vec();
72 for (new_pos, &old_pos) in indices.iter().enumerate() {
73 lines[new_pos] = original[old_pos].clone();
74 }
75 }
76
77 pub fn parallel_hash_sort<T: Clone + Send + Sync>(
79 lines: &mut [T],
80 get_key: impl Fn(&T) -> &[u8] + Sync,
81 ) {
82 if lines.len() < 100_000 {
83 Self::hash_sort(lines, get_key);
85 return;
86 }
87
88 let groups = Self::parallel_hash_group(lines, &get_key);
90
91 let shuffled_indices = Self::create_shuffled_indices(&groups);
93 Self::reorder_by_indices(lines, &shuffled_indices);
94 }
95
96 fn parallel_hash_group<T: Send + Sync>(
98 lines: &[T],
99 get_key: &(impl Fn(&T) -> &[u8] + Sync),
100 ) -> Vec<Vec<usize>> {
101 let _chunk_size = lines.len() / rayon::current_num_threads();
102
103 let hashes: Vec<(usize, u64)> = lines
105 .par_iter()
106 .enumerate()
107 .map(|(idx, line)| {
108 let key = get_key(line);
109 (idx, Self::fast_hash(key))
110 })
111 .collect();
112
113 let mut hash_to_indices: HashMap<u64, Vec<usize>> = HashMap::new();
115 for (idx, hash) in hashes {
116 hash_to_indices.entry(hash).or_default().push(idx);
117 }
118
119 hash_to_indices.into_values().collect()
120 }
121
122 pub fn streaming_random_sort<R, W>(
124 _reader: R,
125 _writer: W,
126 _memory_limit_mb: usize,
127 ) -> std::io::Result<()>
128 where
129 R: std::io::BufRead,
130 W: std::io::Write,
131 {
132 todo!("Streaming implementation")
135 }
136}
137
138#[cfg(target_arch = "x86_64")]
140pub mod simd_hash {
141 use std::arch::x86_64::*;
142
143 #[target_feature(enable = "avx2")]
148 pub unsafe fn simd_hash_avx2(data: &[u8]) -> u64 {
149 let mut hash = 0u64;
150 let mut i = 0;
151
152 while i + 32 <= data.len() {
154 let chunk = _mm256_loadu_si256(data.as_ptr().add(i) as *const __m256i);
155 let mixed = _mm256_xor_si256(chunk, _mm256_set1_epi64x(0x9E3779B97F4A7C15u64 as i64));
157 let vals: [u64; 4] = std::mem::transmute(mixed);
158 hash ^= vals[0].wrapping_mul(vals[1]) ^ vals[2].wrapping_mul(vals[3]);
159 i += 32;
160 }
161
162 while i < data.len() {
164 hash = hash.wrapping_mul(0x9E3779B97F4A7C15u64) ^ (data[i] as u64);
165 i += 1;
166 }
167
168 hash ^= hash >> 33;
170 hash = hash.wrapping_mul(0xC2B2AE3D27D4EB4Fu64);
171 hash ^= hash >> 29;
172 hash
173 }
174}
175
176pub struct ZeroAllocHashSort;
178
179impl ZeroAllocHashSort {
180 pub fn sort_indices_only(count: usize, get_hash: impl Fn(usize) -> u64) -> Vec<usize> {
182 let mut groups: HashMap<u64, Vec<usize>> = HashMap::new();
184 for i in 0..count {
185 let hash = get_hash(i);
186 groups.entry(hash).or_default().push(i);
187 }
188
189 let mut rng = thread_rng();
191 let mut group_vec: Vec<_> = groups.into_values().collect();
192 group_vec.shuffle(&mut rng);
193
194 let mut result = Vec::with_capacity(count);
195 for group in group_vec {
196 result.extend(group);
197 }
198 result
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 #[test]
207 fn test_ultra_random_sort() {
208 let data = ["apple", "banana", "apple", "cherry", "banana"];
209 let mut groups: std::collections::HashMap<&str, Vec<usize>> =
211 std::collections::HashMap::new();
212 for (i, item) in data.iter().enumerate() {
213 groups.entry(*item).or_default().push(i);
214 }
215
216 let mut i = 0;
218 while i < data.len() {
219 let current = data[i];
220 let mut j = i + 1;
221 while j < data.len() && data[j] == current {
222 j += 1;
223 }
224 for item in &data[i..j] {
226 assert_eq!(*item, current);
227 }
228 i = j;
229 }
230 }
231
232 #[test]
233 fn test_performance() {
234 let mut data: Vec<String> = Vec::new();
236 for i in 0..100_000 {
237 data.push(format!("item_{}", i % 100));
238 }
239
240 let start = std::time::Instant::now();
241 let mut groups: std::collections::HashMap<&[u8], Vec<usize>> =
243 std::collections::HashMap::new();
244 for (i, item) in data.iter().enumerate() {
245 groups.entry(item.as_bytes()).or_default().push(i);
246 }
247 let duration = start.elapsed();
248
249 println!("Ultra random sort took: {duration:?}");
250 assert!(duration.as_millis() < 100); }
252}