Skip to main content

moirai_core/executor/
parallelism.rs

1//! Logical parallelism derivation, cached for the process lifetime.
2
3/// Logical processors this process should parallelize across.
4///
5/// Derived once and cached for the process lifetime. The topology cannot
6/// change while the process runs, and deriving it is not cheap: a
7/// `CpuTopology::detect()` call measures 9,935 ns and 77 allocations totalling
8/// 16,480 bytes on a 24-processor host, because it materializes the whole
9/// NUMA and cache-level description to read one count. Callers that need a
10/// worker count per operation must not pay that, so this is the one place the
11/// derivation happens.
12///
13/// `themis` reports the machine's logical processors; `available_parallelism`
14/// is the fallback when no topology is available.
15#[must_use]
16pub fn logical_parallelism() -> usize {
17    #[cfg(feature = "std")]
18    {
19        static CACHED: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
20        *CACHED.get_or_init(detect_logical_parallelism)
21    }
22    #[cfg(not(feature = "std"))]
23    {
24        detect_logical_parallelism()
25    }
26}
27
28fn detect_logical_parallelism() -> usize {
29    #[cfg(feature = "std")]
30    {
31        themis::CpuTopology::detect()
32            .map(|topology| topology.logical_processors())
33            .or_else(|| {
34                std::thread::available_parallelism()
35                    .ok()
36                    .map(std::num::NonZeroUsize::get)
37            })
38            .unwrap_or(1)
39            .max(1)
40    }
41    #[cfg(not(feature = "std"))]
42    {
43        4 // Reasonable default for no_std
44    }
45}