orx_parallel/parameters/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, Default)]
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 #[default]
26 Auto,
27 /// Limits the maximum number of threads that can be used by the parallel execution.
28 ///
29 /// A special case is `NumThreads::Max(NonZeroUsize::new(1).unwrap())`, or equivalently `NumThreads::sequential()`.
30 /// This will lead to a sequential execution of the defined computation on the main thread.
31 /// 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.
32 ///
33 /// Lastly, `NumThreads::Max(t)` where `t >= 2` can be used in the following scenarios:
34 /// * We have a strict limit on the resources that we can use for this computation, even if the hardware has more resources.
35 /// Parallel execution will ensure that `t` will never be exceeded.
36 /// * We have a computation which is extremely time-critical and our benchmarks show that `t` outperforms the `NumThreads::Auto` on the corresponding system.
37 Max(NonZeroUsize),
38}
39
40const SEQUENTIAL_NUM_THREADS: NonZeroUsize = NonZeroUsize::new(1).expect("seq=1 is positive");
41
42impl From<usize> for NumThreads {
43 /// Converts the nonnegative integer to number of threads as follows:
44 ///
45 /// * 0 is converted to `NumThreads::Auto`,
46 /// * `n` is converted to `NumThreads::Max(n)` where `n > 0`.
47 fn from(value: usize) -> Self {
48 match value {
49 0 => Self::Auto,
50 n => Self::Max(NonZeroUsize::new(n).expect("must be positive")),
51 }
52 }
53}
54
55impl NumThreads {
56 /// Equivalent to `NumThreads::Max(NonZeroUsize::new(1).unwrap())`.
57 ///
58 /// This will lead to a sequential execution of the defined computation on the main thread.
59 /// 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.
60 pub const fn sequential() -> Self {
61 NumThreads::Max(SEQUENTIAL_NUM_THREADS)
62 }
63
64 /// Returns true if number of threads is set to 1.
65 ///
66 /// Note that in this case the computation will be executed sequentially using regular iterators.
67 pub fn is_sequential(self) -> bool {
68 matches!(self, Self::Max(n) if n == SEQUENTIAL_NUM_THREADS)
69 }
70}