Skip to content

Commit 9569beb

Browse files
authored
Merge pull request #188 from BafDyce/168-thread-pool-options
Add possibility to control num_threads and stack_size of global thread pool
2 parents 9d79816 + 458a169 commit 9569beb

File tree

4 files changed

+72
-0
lines changed

4 files changed

+72
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ path = "examples/app/headless.rs"
122122
name = "plugin"
123123
path = "examples/app/plugin.rs"
124124

125+
[[example]]
126+
name = "thread_pool_resources"
127+
path = "examples/app/thread_pool_resources.rs"
128+
125129
[[example]]
126130
name = "hot_asset_reloading"
127131
path = "examples/asset/hot_asset_reloading.rs"

crates/bevy_ecs/src/schedule/parallel_executor.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,53 @@ impl ParallelExecutor {
6868
}
6969
}
7070

71+
72+
/// This can be added as an app resource to control the global `rayon::ThreadPool` used by ecs.
73+
// Dev internal note: We cannot directly expose a ThreadPoolBuilder here as it does not implement Send and Sync.
74+
#[derive(Debug, Default, Clone)]
75+
pub struct ParallelExecutorOptions {
76+
/// If some value, we'll set up the thread pool to use at most n threads. See `rayon::ThreadPoolBuilder::num_threads`.
77+
num_threads: Option<usize>,
78+
/// If some value, we'll set up the thread pool's' workers to the given stack size. See `rayon::ThreadPoolBuilder::stack_size`.
79+
stack_size: Option<usize>,
80+
// TODO: Do we also need/want to expose other features (*_handler, etc.)
81+
}
82+
83+
impl ParallelExecutorOptions {
84+
/// Creates a new ParallelExecutorOptions instance
85+
pub fn new() -> Self {
86+
Self::default()
87+
}
88+
89+
/// Sets the num_threads option, using the builder pattern
90+
pub fn with_num_threads(mut self, num_threads: Option<usize>) -> Self {
91+
self.num_threads = num_threads;
92+
self
93+
}
94+
95+
/// Sets the stack_size option, using the builder pattern. WARNING: Only use this if you know what you're doing,
96+
/// otherwise your application may run into stability and performance issues.
97+
pub fn with_stack_size(mut self, stack_size: Option<usize>) -> Self {
98+
self.stack_size = stack_size;
99+
self
100+
}
101+
102+
/// Creates a new ThreadPoolBuilder based on the current options.
103+
pub(crate) fn create_builder(&self) -> rayon::ThreadPoolBuilder {
104+
let mut builder = rayon::ThreadPoolBuilder::new();
105+
106+
if let Some(num_threads) = self.num_threads {
107+
builder = builder.num_threads(num_threads);
108+
}
109+
110+
if let Some(stack_size) = self.stack_size {
111+
builder = builder.stack_size(stack_size);
112+
}
113+
114+
builder
115+
}
116+
}
117+
71118
#[derive(Debug, Clone)]
72119
pub struct ExecutorStage {
73120
/// each system's set of dependencies

crates/bevy_ecs/src/schedule/schedule.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
22
resource::Resources,
3+
schedule::ParallelExecutorOptions,
34
system::{System, SystemId, ThreadLocalExecution},
45
};
56
use bevy_hecs::World;
@@ -169,6 +170,15 @@ impl Schedule {
169170
return;
170171
}
171172

173+
let thread_pool_builder = resources
174+
.get::<ParallelExecutorOptions>()
175+
.map(|options| (*options).clone())
176+
.unwrap_or_else(|| ParallelExecutorOptions::default())
177+
.create_builder();
178+
// For now, bevy_ecs only uses the global thread pool so it is sufficient to configure it once here.
179+
// Dont call .unwrap() as the function is called twice..
180+
let _ = thread_pool_builder.build_global();
181+
172182
for stage in self.stages.values_mut() {
173183
for system in stage.iter_mut() {
174184
let mut system = system.lock().unwrap();

examples/app/thread_pool_resources.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use bevy::{ecs::ParallelExecutorOptions, prelude::*};
2+
use std::time::Duration;
3+
4+
/// This example illustrates how to customize the thread pool used internally (e.g. to only use a
5+
/// certain number of threads).
6+
fn main() {
7+
App::build()
8+
.add_resource(ParallelExecutorOptions::new().with_num_threads(Some(4)))
9+
.add_default_plugins()
10+
.run();
11+
}

0 commit comments

Comments
 (0)