orx_parallel/
num_threads.rs

1use std::num::NonZeroUsize;
2
3/// `NumThreads` represents the degree of parallelization. It is possible to define an upper bound on the number of threads to be used for the parallel computation.
4/// When set to **1**, the computation will be executed sequentially without any overhead.
5/// In this sense, parallel iterators defined in this crate are a union of sequential and parallel execution.
6///
7/// # Rules of Thumb / Guidelines
8///
9/// It is recommended to set this parameter to its default value, `NumThreads::Auto`.
10/// This setting assumes that it can use all available threads; however, the computation will spawn new threads only when required.
11/// In other words, when we can dynamically decide that the task is not large enough to justify spawning a new thread, the parallel execution will avoid it.
12///
13/// A special case is `NumThreads::Max(NonZeroUsize::new(1).unwrap())`, or equivalently `NumThreads::sequential()`.
14/// This will lead to a sequential execution of the defined computation on the main thread.
15/// Both in terms of used resources and computation time, this mode is not similar but **identical** to a sequential execution using the regular sequential `Iterator`s.
16///
17/// Lastly, `NumThreads::Max(t)` where `t >= 2` can be used in the following scenarios:
18/// * We have a strict limit on the resources that we can use for this computation, even if the hardware has more resources.
19///   Parallel execution will ensure that `t` will never be exceeded.
20/// * We have a computation which is extremely time-critical and our benchmarks show that `t` outperforms the `NumThreads::Auto` on the corresponding system.
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum NumThreads {
23    /// This setting assumes that it can use all available threads; however, the computation will spawn new threads only when required.
24    /// In other words, when we can dynamically decide that the task is not large enough to justify spawning a new thread, the parallel execution will avoid it.
25    Auto,
26    /// Limits the maximum number of threads that can be used by the parallel execution.
27    ///
28    /// A special case is `NumThreads::Max(NonZeroUsize::new(1).unwrap())`, or equivalently `NumThreads::sequential()`.
29    /// This will lead to a sequential execution of the defined computation on the main thread.
30    /// Both in terms of used resources and computation time, this mode is not similar but **identical** to a sequential execution using the regular sequential `Iterator`s.
31    ///
32    /// Lastly, `NumThreads::Max(t)` where `t >= 2` can be used in the following scenarios:
33    /// * We have a strict limit on the resources that we can use for this computation, even if the hardware has more resources.
34    ///   Parallel execution will ensure that `t` will never be exceeded.
35    /// * We have a computation which is extremely time-critical and our benchmarks show that `t` outperforms the `NumThreads::Auto` on the corresponding system.
36    Max(NonZeroUsize),
37}
38
39const SEQUENTIAL: NumThreads = NumThreads::Max(unsafe { NonZeroUsize::new_unchecked(1) });
40
41impl Default for NumThreads {
42    /// Default value for number of threads is [`NumThreads::Auto`].
43    fn default() -> Self {
44        Self::Auto
45    }
46}
47
48impl From<usize> for NumThreads {
49    /// Converts the nonnegative integer to number of threads as follows:
50    /// * 0 is converted to `NumThreads::Auto`,
51    /// * `n` where `n > 0` is converted to `NumThreads::Max(n)`.
52    fn from(value: usize) -> Self {
53        match value {
54            0 => Self::Auto,
55            _ => Self::Max(NonZeroUsize::new(value).expect("must be positive")),
56        }
57    }
58}
59
60impl NumThreads {
61    /// Equivalent to `NumThreads::Max(NonZeroUsize::new(1).unwrap())`.
62    ///
63    /// This will lead to a sequential execution of the defined computation on the main thread.
64    /// Both in terms of used resources and computation time, this mode is not similar but **identical** to a sequential execution using the regular sequential `Iterator`s.
65    pub fn sequential() -> Self {
66        SEQUENTIAL
67    }
68}