forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Task System for Bevy (bevyengine#384)
Add bevy_tasks crate to replace rayon
- Loading branch information
1 parent
9f7de20
commit ada462c
Showing
22 changed files
with
847 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
use bevy_ecs::Resources; | ||
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IOTaskPool, TaskPoolBuilder}; | ||
|
||
/// Defines a simple way to determine how many threads to use given the number of remaining cores | ||
/// and number of total cores | ||
#[derive(Clone)] | ||
pub struct TaskPoolThreadAssignmentPolicy { | ||
/// Force using at least this many threads | ||
pub min_threads: usize, | ||
/// Under no circumstance use more than this many threads for this pool | ||
pub max_threads: usize, | ||
/// Target using this percentage of total cores, clamped by min_threads and max_threads. It is | ||
/// permitted to use 1.0 to try to use all remaining threads | ||
pub percent: f32, | ||
} | ||
|
||
impl TaskPoolThreadAssignmentPolicy { | ||
/// Determine the number of threads to use for this task pool | ||
fn get_number_of_threads(&self, remaining_threads: usize, total_threads: usize) -> usize { | ||
assert!(self.percent >= 0.0); | ||
let mut desired = (total_threads as f32 * self.percent).round() as usize; | ||
|
||
// Limit ourselves to the number of cores available | ||
desired = desired.min(remaining_threads); | ||
|
||
// Clamp by min_threads, max_threads. (This may result in us using more threads than are | ||
// available, this is intended. An example case where this might happen is a device with | ||
// <= 2 threads. | ||
bevy_math::clamp(desired, self.min_threads, self.max_threads) | ||
} | ||
} | ||
|
||
/// Helper for configuring and creating the default task pools. For end-users who want full control, | ||
/// insert the default task pools into the resource map manually. If the pools are already inserted, | ||
/// this helper will do nothing. | ||
#[derive(Clone)] | ||
pub struct DefaultTaskPoolOptions { | ||
/// If the number of physical cores is less than min_total_threads, force using min_total_threads | ||
pub min_total_threads: usize, | ||
/// If the number of physical cores is grater than max_total_threads, force using max_total_threads | ||
pub max_total_threads: usize, | ||
|
||
/// Used to determine number of IO threads to allocate | ||
pub io: TaskPoolThreadAssignmentPolicy, | ||
/// Used to determine number of async compute threads to allocate | ||
pub async_compute: TaskPoolThreadAssignmentPolicy, | ||
/// Used to determine number of compute threads to allocate | ||
pub compute: TaskPoolThreadAssignmentPolicy, | ||
} | ||
|
||
impl Default for DefaultTaskPoolOptions { | ||
fn default() -> Self { | ||
DefaultTaskPoolOptions { | ||
// By default, use however many cores are available on the system | ||
min_total_threads: 1, | ||
max_total_threads: std::usize::MAX, | ||
|
||
// Use 25% of cores for IO, at least 1, no more than 4 | ||
io: TaskPoolThreadAssignmentPolicy { | ||
min_threads: 1, | ||
max_threads: 4, | ||
percent: 0.25, | ||
}, | ||
|
||
// Use 25% of cores for async compute, at least 1, no more than 4 | ||
async_compute: TaskPoolThreadAssignmentPolicy { | ||
min_threads: 1, | ||
max_threads: 4, | ||
percent: 0.25, | ||
}, | ||
|
||
// Use all remaining cores for compute (at least 1) | ||
compute: TaskPoolThreadAssignmentPolicy { | ||
min_threads: 1, | ||
max_threads: std::usize::MAX, | ||
percent: 1.0, // This 1.0 here means "whatever is left over" | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl DefaultTaskPoolOptions { | ||
/// Create a configuration that forces using the given number of threads. | ||
pub fn with_num_threads(thread_count: usize) -> Self { | ||
let mut options = Self::default(); | ||
options.min_total_threads = thread_count; | ||
options.max_total_threads = thread_count; | ||
|
||
options | ||
} | ||
|
||
/// Inserts the default thread pools into the given resource map based on the configured values | ||
pub fn create_default_pools(&self, resources: &mut Resources) { | ||
let total_threads = bevy_math::clamp( | ||
bevy_tasks::logical_core_count(), | ||
self.min_total_threads, | ||
self.max_total_threads, | ||
); | ||
|
||
let mut remaining_threads = total_threads; | ||
|
||
if !resources.contains::<IOTaskPool>() { | ||
// Determine the number of IO threads we will use | ||
let io_threads = self | ||
.io | ||
.get_number_of_threads(remaining_threads, total_threads); | ||
remaining_threads -= io_threads; | ||
|
||
resources.insert(IOTaskPool( | ||
TaskPoolBuilder::default() | ||
.num_threads(io_threads) | ||
.thread_name("IO Task Pool".to_string()) | ||
.build(), | ||
)); | ||
} | ||
|
||
if !resources.contains::<AsyncComputeTaskPool>() { | ||
// Determine the number of async compute threads we will use | ||
let async_compute_threads = self | ||
.async_compute | ||
.get_number_of_threads(remaining_threads, total_threads); | ||
remaining_threads -= async_compute_threads; | ||
|
||
resources.insert(AsyncComputeTaskPool( | ||
TaskPoolBuilder::default() | ||
.num_threads(async_compute_threads) | ||
.thread_name("Async Compute Task Pool".to_string()) | ||
.build(), | ||
)); | ||
} | ||
|
||
if !resources.contains::<ComputeTaskPool>() { | ||
// Determine the number of compute threads we will use | ||
// This is intentionally last so that an end user can specify 1.0 as the percent | ||
let compute_threads = self | ||
.compute | ||
.get_number_of_threads(remaining_threads, total_threads); | ||
|
||
resources.insert(ComputeTaskPool( | ||
TaskPoolBuilder::default() | ||
.num_threads(compute_threads) | ||
.thread_name("Compute Task Pool".to_string()) | ||
.build(), | ||
)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.