Skip to main content

ad_plugins_rs/
par_util.rs

1#[cfg(feature = "parallel")]
2use std::sync::OnceLock;
3#[cfg(feature = "parallel")]
4use std::sync::atomic::{AtomicUsize, Ordering};
5
6/// Minimum element count to justify rayon overhead.
7pub const PAR_THRESHOLD: usize = 4096;
8
9/// Number of CPU cores reserved for driver threads, tokio runtime, etc.
10/// The rayon pool will use `available_cores - RESERVED_CORES` threads (minimum 1).
11#[cfg(feature = "parallel")]
12const RESERVED_CORES: usize = 2;
13
14/// Returns true if the data size warrants parallel processing.
15pub fn should_parallelize(num_elements: usize) -> bool {
16    num_elements >= PAR_THRESHOLD
17}
18
19/// Shared rayon ThreadPool.
20///
21/// Plugins in non-blocking mode each have their own data thread, so multiple
22/// plugins may submit rayon work concurrently. A single shared pool ensures
23/// work-stealing without over-subscription.
24///
25/// The pool is sized to `available_cores - RESERVED_CORES` to leave headroom
26/// for port driver data threads, autoconnect tasks, and the tokio runtime.
27/// Call [`set_num_threads`] before the first `thread_pool()` access to override.
28#[cfg(feature = "parallel")]
29static POOL: OnceLock<rayon::ThreadPool> = OnceLock::new();
30
31/// User-specified thread count override. 0 means "use default formula".
32#[cfg(feature = "parallel")]
33static NUM_THREADS_OVERRIDE: AtomicUsize = AtomicUsize::new(0);
34
35/// Set the number of rayon worker threads before the pool is first used.
36///
37/// Must be called before any plugin processes an array. Has no effect if the
38/// pool has already been initialized.
39#[cfg(feature = "parallel")]
40pub fn set_num_threads(n: usize) {
41    NUM_THREADS_OVERRIDE.store(n, Ordering::Relaxed);
42}
43
44#[cfg(feature = "parallel")]
45pub fn thread_pool() -> &'static rayon::ThreadPool {
46    POOL.get_or_init(|| {
47        let user = NUM_THREADS_OVERRIDE.load(Ordering::Relaxed);
48        let num_threads = if user > 0 {
49            user
50        } else {
51            let available = std::thread::available_parallelism()
52                .map(|n| n.get())
53                .unwrap_or(1);
54            available.saturating_sub(RESERVED_CORES).max(1)
55        };
56        rayon::ThreadPoolBuilder::new()
57            .num_threads(num_threads)
58            .build()
59            .expect("failed to create rayon thread pool")
60    })
61}