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}