Skip to main content

math_audio_bem/core/
parallel.rs

1//! Portable parallel iteration abstractions
2//!
3//! This module provides parallel iteration that works across all build targets:
4//! - `native` feature: Uses native rayon for maximum performance
5//! - `wasm` feature: Uses wasm-bindgen-rayon for Web Worker parallelism
6//! - Neither: Falls back to sequential iteration
7//!
8//! ## Usage
9//!
10//! ```ignore
11//! use crate::core::parallel::*;
12//!
13//! // Parallel map over a slice
14//! let results: Vec<i32> = parallel_map(&data, |item| item * 2);
15//!
16//! // Parallel map over indices
17//! let results: Vec<i32> = parallel_map_indexed(100, |i| i * 2);
18//! ```
19
20/// Check if parallel processing is available
21#[inline]
22pub fn is_parallel_available() -> bool {
23    cfg!(any(feature = "native", feature = "wasm"))
24}
25
26/// Parallel map over a slice
27///
28/// When parallel features are enabled, uses rayon's parallel iterator.
29/// Otherwise, falls back to sequential iteration.
30#[cfg(any(feature = "native", feature = "wasm"))]
31pub fn parallel_map<T, U, F>(data: &[T], f: F) -> Vec<U>
32where
33    T: Sync,
34    U: Send,
35    F: Fn(&T) -> U + Sync + Send,
36{
37    use rayon::prelude::*;
38    data.par_iter().map(f).collect()
39}
40
41#[cfg(not(any(feature = "native", feature = "wasm")))]
42pub fn parallel_map<T, U, F>(data: &[T], f: F) -> Vec<U>
43where
44    F: Fn(&T) -> U,
45{
46    data.iter().map(f).collect()
47}
48
49/// Parallel map over a range of indices
50///
51/// When parallel features are enabled, uses rayon's parallel iterator.
52/// Otherwise, falls back to sequential iteration.
53#[cfg(any(feature = "native", feature = "wasm"))]
54pub fn parallel_map_indexed<U, F>(count: usize, f: F) -> Vec<U>
55where
56    U: Send,
57    F: Fn(usize) -> U + Sync + Send,
58{
59    use rayon::prelude::*;
60    (0..count).into_par_iter().map(f).collect()
61}
62
63#[cfg(not(any(feature = "native", feature = "wasm")))]
64pub fn parallel_map_indexed<U, F>(count: usize, f: F) -> Vec<U>
65where
66    F: Fn(usize) -> U,
67{
68    (0..count).map(f).collect()
69}
70
71/// Parallel flat_map over a slice
72#[cfg(any(feature = "native", feature = "wasm"))]
73pub fn parallel_flat_map<T, U, I, F>(data: &[T], f: F) -> Vec<U>
74where
75    T: Sync,
76    U: Send,
77    I: rayon::iter::IntoParallelIterator<Item = U>,
78    F: Fn(&T) -> I + Sync + Send,
79{
80    use rayon::prelude::*;
81    data.par_iter().flat_map(f).collect()
82}
83
84#[cfg(not(any(feature = "native", feature = "wasm")))]
85pub fn parallel_flat_map<T, U, I, F>(data: &[T], f: F) -> Vec<U>
86where
87    I: IntoIterator<Item = U>,
88    F: Fn(&T) -> I,
89{
90    data.iter().flat_map(f).collect()
91}
92
93/// Parallel filter_map over a slice
94#[cfg(any(feature = "native", feature = "wasm"))]
95pub fn parallel_filter_map<T, U, F>(data: &[T], f: F) -> Vec<U>
96where
97    T: Sync,
98    U: Send,
99    F: Fn(&T) -> Option<U> + Sync + Send,
100{
101    use rayon::prelude::*;
102    data.par_iter().filter_map(f).collect()
103}
104
105#[cfg(not(any(feature = "native", feature = "wasm")))]
106pub fn parallel_filter_map<T, U, F>(data: &[T], f: F) -> Vec<U>
107where
108    F: Fn(&T) -> Option<U>,
109{
110    data.iter().filter_map(f).collect()
111}
112
113/// Parallel for_each over a slice
114#[cfg(any(feature = "native", feature = "wasm"))]
115pub fn parallel_for_each<T, F>(data: &[T], f: F)
116where
117    T: Sync,
118    F: Fn(&T) + Sync + Send,
119{
120    use rayon::prelude::*;
121    data.par_iter().for_each(f);
122}
123
124#[cfg(not(any(feature = "native", feature = "wasm")))]
125pub fn parallel_for_each<T, F>(data: &[T], f: F)
126where
127    F: Fn(&T),
128{
129    data.iter().for_each(f);
130}
131
132/// Parallel enumerate and map over a slice
133#[cfg(any(feature = "native", feature = "wasm"))]
134pub fn parallel_enumerate_map<T, U, F>(data: &[T], f: F) -> Vec<U>
135where
136    T: Sync,
137    U: Send,
138    F: Fn(usize, &T) -> U + Sync + Send,
139{
140    use rayon::prelude::*;
141    data.par_iter().enumerate().map(|(i, x)| f(i, x)).collect()
142}
143
144#[cfg(not(any(feature = "native", feature = "wasm")))]
145pub fn parallel_enumerate_map<T, U, F>(data: &[T], f: F) -> Vec<U>
146where
147    F: Fn(usize, &T) -> U,
148{
149    data.iter().enumerate().map(|(i, x)| f(i, x)).collect()
150}
151
152/// Parallel enumerate and filter_map over a slice
153#[cfg(any(feature = "native", feature = "wasm"))]
154pub fn parallel_enumerate_filter_map<T, U, F>(data: &[T], f: F) -> Vec<U>
155where
156    T: Sync,
157    U: Send,
158    F: Fn(usize, &T) -> Option<U> + Sync + Send,
159{
160    use rayon::prelude::*;
161    data.par_iter()
162        .enumerate()
163        .filter_map(|(i, x)| f(i, x))
164        .collect()
165}
166
167#[cfg(not(any(feature = "native", feature = "wasm")))]
168pub fn parallel_enumerate_filter_map<T, U, F>(data: &[T], f: F) -> Vec<U>
169where
170    F: Fn(usize, &T) -> Option<U>,
171{
172    data.iter()
173        .enumerate()
174        .filter_map(|(i, x)| f(i, x))
175        .collect()
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn test_parallel_map() {
184        let data = vec![1, 2, 3, 4, 5];
185        let result = parallel_map(&data, |x| x * 2);
186        assert_eq!(result, vec![2, 4, 6, 8, 10]);
187    }
188
189    #[test]
190    fn test_parallel_map_indexed() {
191        let result = parallel_map_indexed(5, |i| i * 2);
192        assert_eq!(result, vec![0, 2, 4, 6, 8]);
193    }
194
195    #[test]
196    fn test_parallel_filter_map() {
197        let data = vec![1, 2, 3, 4, 5];
198        let result = parallel_filter_map(&data, |x| if *x % 2 == 0 { Some(*x) } else { None });
199        assert_eq!(result, vec![2, 4]);
200    }
201
202    #[test]
203    fn test_parallel_enumerate_map() {
204        let data = vec![10, 20, 30];
205        let result = parallel_enumerate_map(&data, |i, x| i + *x);
206        assert_eq!(result, vec![10, 21, 32]);
207    }
208}