Skip to main content

qsm_core/
par.rs

1//! Conditional parallelism macros.
2//!
3//! When the `parallel` feature is enabled, these macros expand to rayon's
4//! parallel iterators. Without the feature, they expand to standard iterators.
5//! This allows algorithm code to use `maybe_par_iter!(data)` and get parallelism
6//! on native targets while remaining single-threaded for WASM.
7
8#[cfg(feature = "parallel")]
9pub use rayon::prelude::*;
10
11/// Parallel or sequential immutable iterator over a slice.
12#[cfg(feature = "parallel")]
13#[macro_export]
14macro_rules! maybe_par_iter {
15    ($slice:expr) => {
16        $slice.par_iter()
17    };
18}
19
20#[cfg(not(feature = "parallel"))]
21#[macro_export]
22macro_rules! maybe_par_iter {
23    ($slice:expr) => {
24        $slice.iter()
25    };
26}
27
28/// Parallel or sequential mutable iterator over a slice.
29#[cfg(feature = "parallel")]
30#[macro_export]
31macro_rules! maybe_par_iter_mut {
32    ($slice:expr) => {
33        $slice.par_iter_mut()
34    };
35}
36
37#[cfg(not(feature = "parallel"))]
38#[macro_export]
39macro_rules! maybe_par_iter_mut {
40    ($slice:expr) => {
41        $slice.iter_mut()
42    };
43}
44
45/// Parallel or sequential chunks iterator.
46#[cfg(feature = "parallel")]
47#[macro_export]
48macro_rules! maybe_par_chunks_mut {
49    ($slice:expr, $chunk_size:expr) => {
50        $slice.par_chunks_mut($chunk_size)
51    };
52}
53
54#[cfg(not(feature = "parallel"))]
55#[macro_export]
56macro_rules! maybe_par_chunks_mut {
57    ($slice:expr, $chunk_size:expr) => {
58        $slice.chunks_mut($chunk_size)
59    };
60}
61
62/// Parallel or sequential (immutable) chunks iterator.
63///
64/// Useful for deterministic parallel reductions: map each fixed-size chunk to a
65/// sequential partial sum, then combine the partials in index order. Because the
66/// chunk boundaries and combination order are fixed, the result is independent of
67/// the thread count (bit-for-bit reproducible).
68#[cfg(feature = "parallel")]
69#[macro_export]
70macro_rules! maybe_par_chunks {
71    ($slice:expr, $chunk_size:expr) => {
72        $slice.par_chunks($chunk_size)
73    };
74}
75
76#[cfg(not(feature = "parallel"))]
77#[macro_export]
78macro_rules! maybe_par_chunks {
79    ($slice:expr, $chunk_size:expr) => {
80        $slice.chunks($chunk_size)
81    };
82}
83
84/// Parallel or sequential `map` with a reusable mutable state produced by
85/// `$init`.
86///
87/// Lets a hot per-item body hoist its scratch buffers out of the loop: with
88/// rayon each worker thread gets its own state and reuses it across the items
89/// it steals, and without the feature a single state is reused for the whole
90/// iteration. The per-item body must fully overwrite whatever it reads from the
91/// state, so results stay independent of the thread count.
92#[cfg(feature = "parallel")]
93#[macro_export]
94macro_rules! maybe_par_map_init {
95    ($slice:expr, $init:expr, $f:expr) => {
96        $slice.par_iter().map_init($init, $f)
97    };
98}
99
100#[cfg(not(feature = "parallel"))]
101#[macro_export]
102macro_rules! maybe_par_map_init {
103    ($slice:expr, $init:expr, $f:expr) => {{
104        let mut state = ($init)();
105        let mut f = $f;
106        $slice.iter().map(move |item| f(&mut state, item))
107    }};
108}