From 0c69e2a021811ba151c803af3c671923f3f1dd0e Mon Sep 17 00:00:00 2001 From: James Liu Date: Mon, 19 Sep 2022 15:46:03 +0000 Subject: [PATCH] Swap out num_cpus for std::thread::available_parallelism (#4970) # Objective As of Rust 1.59, `std::thread::available_parallelism` has been stabilized. As of Rust 1.61, the API matches `num_cpus::get` by properly handling Linux's cgroups and other sandboxing mechanisms. As bevy does not have an established MSRV, we can replace `num_cpus` in `bevy_tasks` and reduce our dependency tree by one dep. ## Solution Replace `num_cpus` with `std::thread::available_parallelism`. Wrap it to have a fallback in the case it errors out and have it operate in the same manner as `num_cpus` did. This however removes `physical_core_count` from the API, though we are currently not using it in any way in first-party crates. --- ## Changelog Changed: `bevy_tasks::logical_core_count` -> `bevy_tasks::available_parallelism`. Removed: `bevy_tasks::physical_core_count`. ## Migration Guide `bevy_tasks::logical_core_count` and `bevy_tasks::physical_core_count` have been removed. `logical_core_count` has been replaced with `bevy_tasks::available_parallelism`, which works identically. If `bevy_tasks::physical_core_count` is required, the `num_cpus` crate can be used directly, as these two were just aliases for `num_cpus` APIs. --- crates/bevy_core/src/task_pool_options.rs | 4 ++-- crates/bevy_tasks/Cargo.toml | 1 - crates/bevy_tasks/src/lib.rs | 15 +++++++++++++-- crates/bevy_tasks/src/task_pool.rs | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/bevy_core/src/task_pool_options.rs b/crates/bevy_core/src/task_pool_options.rs index af910b1138bce..43779a072c0a8 100644 --- a/crates/bevy_core/src/task_pool_options.rs +++ b/crates/bevy_core/src/task_pool_options.rs @@ -94,8 +94,8 @@ impl DefaultTaskPoolOptions { /// Inserts the default thread pools into the given resource map based on the configured values pub fn create_default_pools(&self) { - let total_threads = - bevy_tasks::logical_core_count().clamp(self.min_total_threads, self.max_total_threads); + let total_threads = bevy_tasks::available_parallelism() + .clamp(self.min_total_threads, self.max_total_threads); trace!("Assigning {} cores to default task pools", total_threads); let mut remaining_threads = total_threads; diff --git a/crates/bevy_tasks/Cargo.toml b/crates/bevy_tasks/Cargo.toml index 41866500492a5..91f1e8e5e334c 100644 --- a/crates/bevy_tasks/Cargo.toml +++ b/crates/bevy_tasks/Cargo.toml @@ -12,7 +12,6 @@ keywords = ["bevy"] futures-lite = "1.4.0" async-executor = "1.3.0" async-channel = "1.4.2" -num_cpus = "1" once_cell = "1.7" [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index 7345d775ee968..62c42a7717868 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -33,5 +33,16 @@ pub mod prelude { }; } -pub use num_cpus::get as logical_core_count; -pub use num_cpus::get_physical as physical_core_count; +use std::num::NonZeroUsize; + +/// Gets the logical CPU core count available to the current process. +/// +/// This is identical to [`std::thread::available_parallelism`], except +/// it will return a default value of 1 if it internally errors out. +/// +/// This will always return at least 1. +pub fn available_parallelism() -> usize { + std::thread::available_parallelism() + .map(NonZeroUsize::get) + .unwrap_or(1) +} diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index b52ce0b9f1445..c9c97322f56f7 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -95,7 +95,7 @@ impl TaskPool { let executor = Arc::new(async_executor::Executor::new()); - let num_threads = num_threads.unwrap_or_else(num_cpus::get); + let num_threads = num_threads.unwrap_or_else(crate::available_parallelism); let threads = (0..num_threads) .map(|i| {