mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Stepping disabled performance fix (#11959)
# Objective * Fixes #11932 (performance impact when stepping is disabled) ## Solution The `Option<FixedBitSet>` argument added to `ScheduleExecutor::run()` in #8453 caused a measurable performance impact even when stepping is disabled. This can be seen by the benchmark of running `Schedule:run()` on an empty schedule in a tight loop (https://github.com/bevyengine/bevy/issues/11932#issuecomment-1950970236). I was able to get the same performance results as on 0.12.1 by changing the argument `ScheduleExecutor::run()` from `Option<FixedBitSet>` to `Option<&FixedBitSet>`. The down-side of this change is that `Schedule::run()` now takes about 6% longer (3.7319 ms vs 3.9855ns) when stepping is enabled --- ## Changelog * Change `ScheduleExecutor::run()` `_skipped_systems` from `Option<FixedBitSet>` to `Option<&FixedBitSet>` * Added a few benchmarks to measure `Schedule::run()` performance with various executors
This commit is contained in:
parent
2a3e367b82
commit
5d9c9b85d5
7 changed files with 44 additions and 23 deletions
|
@ -19,4 +19,5 @@ criterion_group!(
|
|||
contrived,
|
||||
schedule,
|
||||
build_schedule,
|
||||
empty_schedule_run,
|
||||
);
|
||||
|
|
|
@ -118,3 +118,28 @@ pub fn build_schedule(criterion: &mut Criterion) {
|
|||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
pub fn empty_schedule_run(criterion: &mut Criterion) {
|
||||
let mut app = bevy_app::App::default();
|
||||
|
||||
let mut group = criterion.benchmark_group("run_empty_schedule");
|
||||
|
||||
let mut schedule = Schedule::default();
|
||||
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded);
|
||||
group.bench_function("SingleThreaded", |bencher| {
|
||||
bencher.iter(|| schedule.run(&mut app.world));
|
||||
});
|
||||
|
||||
let mut schedule = Schedule::default();
|
||||
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::MultiThreaded);
|
||||
group.bench_function("MultiThreaded", |bencher| {
|
||||
bencher.iter(|| schedule.run(&mut app.world));
|
||||
});
|
||||
|
||||
let mut schedule = Schedule::default();
|
||||
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::Simple);
|
||||
group.bench_function("Simple", |bencher| {
|
||||
bencher.iter(|| schedule.run(&mut app.world));
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ pub(super) trait SystemExecutor: Send + Sync {
|
|||
fn run(
|
||||
&mut self,
|
||||
schedule: &mut SystemSchedule,
|
||||
skip_systems: Option<FixedBitSet>,
|
||||
world: &mut World,
|
||||
skip_systems: Option<&FixedBitSet>,
|
||||
);
|
||||
fn set_apply_final_deferred(&mut self, value: bool);
|
||||
}
|
||||
|
|
|
@ -166,8 +166,8 @@ impl SystemExecutor for MultiThreadedExecutor {
|
|||
fn run(
|
||||
&mut self,
|
||||
schedule: &mut SystemSchedule,
|
||||
_skip_systems: Option<FixedBitSet>,
|
||||
world: &mut World,
|
||||
_skip_systems: Option<&FixedBitSet>,
|
||||
) {
|
||||
// reset counts
|
||||
self.num_systems = schedule.systems.len();
|
||||
|
@ -189,26 +189,18 @@ impl SystemExecutor for MultiThreadedExecutor {
|
|||
// If stepping is enabled, make sure we skip those systems that should
|
||||
// not be run.
|
||||
#[cfg(feature = "bevy_debug_stepping")]
|
||||
if let Some(mut skipped_systems) = _skip_systems {
|
||||
if let Some(skipped_systems) = _skip_systems {
|
||||
debug_assert_eq!(skipped_systems.len(), self.completed_systems.len());
|
||||
// mark skipped systems as completed
|
||||
self.completed_systems |= &skipped_systems;
|
||||
self.completed_systems |= skipped_systems;
|
||||
self.num_completed_systems = self.completed_systems.count_ones(..);
|
||||
|
||||
// signal the dependencies for each of the skipped systems, as
|
||||
// though they had run
|
||||
for system_index in skipped_systems.ones() {
|
||||
self.signal_dependents(system_index);
|
||||
self.ready_systems.set(system_index, false);
|
||||
}
|
||||
|
||||
// Finally, we need to clear all skipped systems from the ready
|
||||
// list.
|
||||
//
|
||||
// We invert the skipped system mask to get the list of systems
|
||||
// that should be run. Then we bitwise AND it with the ready list,
|
||||
// resulting in a list of ready systems that aren't skipped.
|
||||
skipped_systems.toggle_range(..);
|
||||
self.ready_systems &= skipped_systems;
|
||||
}
|
||||
|
||||
let thread_executor = world
|
||||
|
|
|
@ -33,15 +33,15 @@ impl SystemExecutor for SimpleExecutor {
|
|||
fn run(
|
||||
&mut self,
|
||||
schedule: &mut SystemSchedule,
|
||||
_skip_systems: Option<FixedBitSet>,
|
||||
world: &mut World,
|
||||
_skip_systems: Option<&FixedBitSet>,
|
||||
) {
|
||||
// If stepping is enabled, make sure we skip those systems that should
|
||||
// not be run.
|
||||
#[cfg(feature = "bevy_debug_stepping")]
|
||||
if let Some(skipped_systems) = _skip_systems {
|
||||
// mark skipped systems as completed
|
||||
self.completed_systems |= &skipped_systems;
|
||||
self.completed_systems |= skipped_systems;
|
||||
}
|
||||
|
||||
for system_index in 0..schedule.systems.len() {
|
||||
|
|
|
@ -41,15 +41,15 @@ impl SystemExecutor for SingleThreadedExecutor {
|
|||
fn run(
|
||||
&mut self,
|
||||
schedule: &mut SystemSchedule,
|
||||
_skip_systems: Option<FixedBitSet>,
|
||||
world: &mut World,
|
||||
_skip_systems: Option<&FixedBitSet>,
|
||||
) {
|
||||
// If stepping is enabled, make sure we skip those systems that should
|
||||
// not be run.
|
||||
#[cfg(feature = "bevy_debug_stepping")]
|
||||
if let Some(skipped_systems) = _skip_systems {
|
||||
// mark skipped systems as completed
|
||||
self.completed_systems |= &skipped_systems;
|
||||
self.completed_systems |= skipped_systems;
|
||||
}
|
||||
|
||||
for system_index in 0..schedule.systems.len() {
|
||||
|
|
|
@ -333,15 +333,18 @@ impl Schedule {
|
|||
.unwrap_or_else(|e| panic!("Error when initializing schedule {:?}: {e}", self.label));
|
||||
|
||||
#[cfg(not(feature = "bevy_debug_stepping"))]
|
||||
let skip_systems = None;
|
||||
self.executor.run(&mut self.executable, world, None);
|
||||
|
||||
#[cfg(feature = "bevy_debug_stepping")]
|
||||
let skip_systems = match world.get_resource_mut::<Stepping>() {
|
||||
None => None,
|
||||
Some(mut stepping) => stepping.skipped_systems(self),
|
||||
};
|
||||
{
|
||||
let skip_systems = match world.get_resource_mut::<Stepping>() {
|
||||
None => None,
|
||||
Some(mut stepping) => stepping.skipped_systems(self),
|
||||
};
|
||||
|
||||
self.executor.run(&mut self.executable, skip_systems, world);
|
||||
self.executor
|
||||
.run(&mut self.executable, world, skip_systems.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes any newly-added systems and conditions, rebuilds the executable schedule,
|
||||
|
|
Loading…
Reference in a new issue