orx_parallel/runner/new_runner.rs
1use crate::ThreadPool;
2use crate::pools::DefaultPool;
3use crate::pools::global_pool;
4#[cfg(feature = "std")]
5use crate::runner::runner_variants::AdaptiveChunkRunner;
6use crate::runner::runner_variants::FixedChunkRunner;
7
8/// Entry point for creating parallel runners that control how work is distributed across threads.
9///
10/// A runner is passed to `.runner(...)` on a parallel iterator to select the execution strategy.
11///
12/// > **Note:** `Runner` is a convenience factory for the runners provided by this crate.
13/// > You can also implement a compatible runner type yourself and pass it directly to `.runner(...)` —
14/// > the transformation accepts any type that satisfies the trait.
15///
16/// # Examples
17///
18/// ```rust
19/// use orx_parallel::*;
20///
21/// let par = (0..100).par().map(|x| x + 1);
22/// let par = par.runner(Runner::fixed());
23/// let sum = par.sum();
24///
25/// let par = (0..100).par().map(|x| x + 1);
26/// #[cfg(feature = "std")]
27/// let par = par.runner(Runner::adaptive());
28/// let sum = par.sum();
29/// ```
30pub struct Runner;
31
32impl Runner {
33 /// Creates a runner that splits work into fixed-size chunks ahead of time.
34 ///
35 /// This is the default strategy: the input is divided into equal chunks, one per thread.
36 /// It has low overhead and works well when tasks have uniform cost.
37 ///
38 /// # Example
39 ///
40 /// ```
41 /// use orx_parallel::*;
42 ///
43 /// let par = (0..100).par().map(|x| x + 1);
44 /// let par = par.runner(Runner::fixed());
45 ///
46 /// let result: Vec<_> = par.collect();
47 /// ```
48 pub fn fixed() -> FixedChunkRunner<DefaultPool> {
49 FixedChunkRunner::new(global_pool())
50 }
51
52 /// Creates a fixed chunk runner backed by `pool`.
53 ///
54 /// Use this when a computation should use a specific thread pool instead of the global
55 /// default pool. The returned runner keeps the fixed-size chunking strategy of
56 /// [`Self::fixed`] while delegating execution to the provided pool.
57 ///
58 /// # Example
59 ///
60 /// ```
61 /// use orx_parallel::*;
62 ///
63 /// let par = (0..100).par();
64 ///
65 /// #[cfg(feature = "std")]
66 /// let par = par.runner(Runner::fixed_with_pool(Pool::basic(4)));
67 ///
68 /// let result: Vec<_> = par.collect();
69 /// ```
70 pub fn fixed_with_pool<P: ThreadPool>(pool: P) -> FixedChunkRunner<P> {
71 FixedChunkRunner::new(pool)
72 }
73
74 /// Creates an adaptive chunk runner.
75 ///
76 /// This strategy explores and selects chunk sizes based on observed runtime behavior.
77 ///
78 /// # Example
79 ///
80 /// ```
81 /// use orx_parallel::*;
82 ///
83 /// let par = (0..100).par().map(|x| x + 1);
84 ///
85 /// #[cfg(feature = "std")]
86 /// let par = par.runner(Runner::adaptive());
87 ///
88 /// let result: Vec<_> = par.collect();
89 /// ```
90 #[cfg(feature = "std")]
91 pub fn adaptive() -> AdaptiveChunkRunner<DefaultPool> {
92 AdaptiveChunkRunner::new(global_pool())
93 }
94
95 /// Creates an adaptive chunk runner backed by `pool`.
96 ///
97 /// Use this when a computation should combine a specific thread pool with adaptive chunk
98 /// sizing. The returned runner keeps the adaptive strategy of [`Self::adaptive`] while
99 /// delegating execution to the provided pool.
100 ///
101 /// # Example
102 ///
103 /// ```
104 /// use orx_parallel::*;
105 ///
106 /// let pool = Pool::once(4);
107 /// let par = (0..100).par().runner(Runner::adaptive_with_pool(pool));
108 ///
109 /// let result: Vec<_> = par.collect();
110 /// ```
111 #[cfg(feature = "std")]
112 pub fn adaptive_with_pool<P: ThreadPool>(pool: P) -> AdaptiveChunkRunner<P> {
113 AdaptiveChunkRunner::new(pool)
114 }
115}