From 2ad402b44eac5616462c58d442dc7f0b4e408ba6 Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 01:36:03 -0400 Subject: [PATCH 1/8] add realistic benchmarks --- .../benches/bevy_ecs/scheduling/schedule.rs | 463 +++++++++++++++--- 1 file changed, 404 insertions(+), 59 deletions(-) diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 49de37079b73b..97036b2936e07 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -1,6 +1,6 @@ -use bevy_app::App; use bevy_ecs::prelude::*; use criterion::Criterion; +use rand::RngCore; pub fn schedule(c: &mut Criterion) { #[derive(Component)] @@ -57,75 +57,420 @@ pub fn schedule(c: &mut Criterion) { group.finish(); } +/// performs takes a value out of a reference, applies a fn, and puts it back in. +/// stores a temporary dummy value while performing the operation. +fn map_with_temp(ptr: &mut T, temp: T, f: impl FnOnce(T) -> T) { + let val = std::mem::replace(ptr, temp); + *ptr = f(val); +} + pub fn build_schedule(criterion: &mut Criterion) { - // empty system - fn empty_system() {} - - // Use multiple different kinds of label to ensure that dynamic dispatch - // doesn't somehow get optimized away. - #[derive(Debug, Clone, Copy)] - struct NumLabel(usize); - #[derive(Debug, Clone, Copy, SystemLabel)] - struct DummyLabel; - - impl SystemLabel for NumLabel { - fn as_str(&self) -> &'static str { - let s = self.0.to_string(); - Box::leak(s.into_boxed_str()) - } + use bevy_ecs::{ + prelude::*, + schedule::{ParallelSystemDescriptor, SystemLabelId}, + }; + + // Simulates a plugin that has a decent number of systems. + // Systems have interdependencies within plugins, + // as well as with public labels exported by other plugins. + // Also, sometimes entire plugins have dependencies with one another, via the plugin's own label. + struct Plugin { + label: SystemLabelId, + systems: [ParallelSystemDescriptor; 20], + pub_labels: [SystemLabelId; 4], } - let mut group = criterion.benchmark_group("build_schedule"); - group.warm_up_time(std::time::Duration::from_millis(500)); - group.measurement_time(std::time::Duration::from_secs(15)); + #[derive(SystemLabel)] + struct PluginLabel; + + #[derive(SystemLabel)] + enum PubLabel { + Short, + LongName, + ReallyLongName, + ReallyReallySuperLongName, + } + + fn my_system() {} + + // chance of there being a dependency between any two plugins. + const PLUGIN_DEP_CHANCE: u32 = 5; + // chance of there being a dependency between any two systems within a plugin. + const INNER_DEP_CHANCE: u32 = 30; + // chance for each system in a plugin to have any given public label. + const PUB_LABEL_CHANCE: u32 = 25; + // chance of there being a dependency between any system and another plugin's public labels + const OUTER_DEP_CHANCE: u32 = 10; + + impl Plugin { + fn new() -> Self { + let plugin_label = PluginLabel::.as_label(); + + let pub_labels = [ + PubLabel::::Short.as_label(), + PubLabel::::LongName.as_label(), + PubLabel::::ReallyLongName.as_label(), + PubLabel::::ReallyReallySuperLongName.as_label(), + ]; - // Method: generate a set of `graph_size` systems which have a One True Ordering. - // Add system to the stage with full constraints. Hopefully this should be maximimally - // difficult for bevy to figure out. - // Also, we are performing the `as_label` operation outside of the loop since that - // requires an allocation and a leak. This is not something that would be necessary in a - // real scenario, just a contrivance for the benchmark. - let labels: Vec<_> = (0..1000).map(|i| NumLabel(i).as_label()).collect(); - - // Benchmark graphs of different sizes. - for graph_size in [100, 500, 1000] { - // Basic benchmark without constraints. - group.bench_function(format!("{graph_size}_schedule_noconstraints"), |bencher| { - bencher.iter(|| { - let mut app = App::new(); - for _ in 0..graph_size { - app.add_system(empty_system); + let systems = [ + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + my_system::, + ]; + let systems = systems.map(|s| s.label(plugin_label)); + + let mut rng = rand::thread_rng(); + + let mut i = 0; + let systems = systems.map(|mut system| { + macro_rules! maybe_dep { + ($P:ident, $J:literal) => { + if i != $J && rng.next_u32() % 100 < INNER_DEP_CHANCE { + if i < $J { + system = system.before(my_system::<$P, $J>); + } else { + system = system.after(my_system::<$P, $J>); + } + } + }; } - app.update(); + + // have a chance to form a dependency with every other system in this plugin. + maybe_dep!(I, 0); + maybe_dep!(I, 1); + maybe_dep!(I, 2); + maybe_dep!(I, 3); + maybe_dep!(I, 4); + maybe_dep!(I, 5); + maybe_dep!(I, 6); + maybe_dep!(I, 7); + maybe_dep!(I, 8); + maybe_dep!(I, 9); + maybe_dep!(I, 10); + maybe_dep!(I, 11); + maybe_dep!(I, 12); + maybe_dep!(I, 13); + maybe_dep!(I, 14); + maybe_dep!(I, 15); + maybe_dep!(I, 16); + maybe_dep!(I, 17); + maybe_dep!(I, 18); + maybe_dep!(I, 19); + + // have a chance to add public labels. + for &label in &pub_labels { + if rng.next_u32() % 100 < PUB_LABEL_CHANCE { + system = system.label(label); + } + } + + i += 1; + + system }); - }); - // Benchmark with constraints. - group.bench_function(format!("{graph_size}_schedule"), |bencher| { - bencher.iter(|| { - let mut app = App::new(); - app.add_system(empty_system.label(DummyLabel)); - - // Build a fully-connected dependency graph describing the One True Ordering. - // Not particularly realistic but this can be refined later. - for i in 0..graph_size { - let mut sys = empty_system.label(labels[i]).before(DummyLabel); - for a in 0..i { - sys = sys.after(labels[a]); + Self { + label: plugin_label, + systems, + pub_labels, + } + } + } + + // simulates an app with many plugins. + struct Experiment { + plugins: Vec, + } + + impl Experiment { + fn new(plugins: impl IntoIterator) -> Self { + let mut plugins: Vec<_> = plugins.into_iter().collect(); + + let mut rng = rand::thread_rng(); + + for i in 0..plugins.len() { + let (before, after) = plugins.split_at_mut(i); + let (plugin, after) = after.split_first_mut().unwrap(); + + for other in before.iter() { + if rng.next_u32() % 100 < PLUGIN_DEP_CHANCE { + for system in &mut plugin.systems { + map_with_temp(system, my_system::<0, 0>.label(PluginLabel::<0>), |s| { + s.after(other.label) + }); + } } - for b in i + 1..graph_size { - sys = sys.before(labels[b]); + } + for other in after.iter() { + if rng.next_u32() % 100 < PLUGIN_DEP_CHANCE { + for system in &mut plugin.systems { + map_with_temp(system, my_system::<0, 0>.label(PluginLabel::<0>), |s| { + s.before(other.label) + }); + } } - app.add_system(sys); } - // Run the app for a single frame. - // This is necessary since dependency resolution does not occur until the game runs. - // FIXME: Running the game clutters up the benchmarks, so ideally we'd be - // able to benchmark the dependency resolution directly. - app.update(); - }); - }); + + for system in &mut plugin.systems { + for &other_label in before.iter().flat_map(|other| &other.pub_labels) { + if rng.next_u32() % 100 < OUTER_DEP_CHANCE { + map_with_temp(system, my_system::<0, 0>.label(PluginLabel::<0>), |s| { + s.after(other_label) + }); + } + } + for &other_label in after.iter().flat_map(|other| &other.pub_labels) { + if rng.next_u32() % 100 < OUTER_DEP_CHANCE { + map_with_temp(system, my_system::<0, 0>.label(PluginLabel::<0>), |s| { + s.before(other_label) + }); + } + } + } + } + + Self { plugins } + } + fn write_to(self, stage: &mut SystemStage) { + for plugin in self.plugins { + for system in plugin.systems { + stage.add_system(system); + } + } + } } + let mut group = criterion.benchmark_group("build_schedule"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(15)); + + group.bench_function("schedule 10 plugins", |bencher| { + let mut world = World::new(); + bencher.iter_batched( + || { + // these must be pushed one by one to avoid overflowing the stack. + let mut plugins = Vec::with_capacity(10); + plugins.push(Plugin::new::<0>()); + plugins.push(Plugin::new::<1>()); + plugins.push(Plugin::new::<2>()); + plugins.push(Plugin::new::<3>()); + plugins.push(Plugin::new::<4>()); + plugins.push(Plugin::new::<5>()); + plugins.push(Plugin::new::<6>()); + plugins.push(Plugin::new::<7>()); + plugins.push(Plugin::new::<8>()); + plugins.push(Plugin::new::<9>()); + Experiment::new(plugins) + }, + |experiment| { + let mut stage = SystemStage::parallel(); + experiment.write_to(&mut stage); + stage.run(&mut world); + }, + criterion::BatchSize::SmallInput, + ); + }); + + group.bench_function("schedule 50 plugins", |bencher| { + let mut world = World::new(); + bencher.iter_batched( + || { + let mut plugins = Vec::with_capacity(10); + plugins.push(Plugin::new::<0>()); + plugins.push(Plugin::new::<1>()); + plugins.push(Plugin::new::<2>()); + plugins.push(Plugin::new::<3>()); + plugins.push(Plugin::new::<4>()); + plugins.push(Plugin::new::<5>()); + plugins.push(Plugin::new::<6>()); + plugins.push(Plugin::new::<7>()); + plugins.push(Plugin::new::<8>()); + plugins.push(Plugin::new::<9>()); + plugins.push(Plugin::new::<10>()); + plugins.push(Plugin::new::<11>()); + plugins.push(Plugin::new::<12>()); + plugins.push(Plugin::new::<13>()); + plugins.push(Plugin::new::<14>()); + plugins.push(Plugin::new::<15>()); + plugins.push(Plugin::new::<16>()); + plugins.push(Plugin::new::<17>()); + plugins.push(Plugin::new::<18>()); + plugins.push(Plugin::new::<19>()); + plugins.push(Plugin::new::<20>()); + plugins.push(Plugin::new::<21>()); + plugins.push(Plugin::new::<22>()); + plugins.push(Plugin::new::<23>()); + plugins.push(Plugin::new::<24>()); + plugins.push(Plugin::new::<25>()); + plugins.push(Plugin::new::<26>()); + plugins.push(Plugin::new::<27>()); + plugins.push(Plugin::new::<28>()); + plugins.push(Plugin::new::<29>()); + plugins.push(Plugin::new::<30>()); + plugins.push(Plugin::new::<31>()); + plugins.push(Plugin::new::<32>()); + plugins.push(Plugin::new::<33>()); + plugins.push(Plugin::new::<34>()); + plugins.push(Plugin::new::<35>()); + plugins.push(Plugin::new::<36>()); + plugins.push(Plugin::new::<37>()); + plugins.push(Plugin::new::<38>()); + plugins.push(Plugin::new::<39>()); + plugins.push(Plugin::new::<40>()); + plugins.push(Plugin::new::<41>()); + plugins.push(Plugin::new::<42>()); + plugins.push(Plugin::new::<43>()); + plugins.push(Plugin::new::<44>()); + plugins.push(Plugin::new::<45>()); + plugins.push(Plugin::new::<46>()); + plugins.push(Plugin::new::<47>()); + plugins.push(Plugin::new::<48>()); + plugins.push(Plugin::new::<49>()); + plugins.push(Plugin::new::<50>()); + Experiment::new(plugins) + }, + |experiment| { + let mut stage = SystemStage::parallel(); + experiment.write_to(&mut stage); + stage.run(&mut world); + }, + criterion::BatchSize::SmallInput, + ); + }); + + group.bench_function("schedule 100 plugins", |bencher| { + let mut world = World::new(); + bencher.iter_batched( + || { + let mut plugins = Vec::with_capacity(10); + plugins.push(Plugin::new::<0>()); + plugins.push(Plugin::new::<1>()); + plugins.push(Plugin::new::<2>()); + plugins.push(Plugin::new::<3>()); + plugins.push(Plugin::new::<4>()); + plugins.push(Plugin::new::<5>()); + plugins.push(Plugin::new::<6>()); + plugins.push(Plugin::new::<7>()); + plugins.push(Plugin::new::<8>()); + plugins.push(Plugin::new::<9>()); + plugins.push(Plugin::new::<10>()); + plugins.push(Plugin::new::<11>()); + plugins.push(Plugin::new::<12>()); + plugins.push(Plugin::new::<13>()); + plugins.push(Plugin::new::<14>()); + plugins.push(Plugin::new::<15>()); + plugins.push(Plugin::new::<16>()); + plugins.push(Plugin::new::<17>()); + plugins.push(Plugin::new::<18>()); + plugins.push(Plugin::new::<19>()); + plugins.push(Plugin::new::<20>()); + plugins.push(Plugin::new::<21>()); + plugins.push(Plugin::new::<22>()); + plugins.push(Plugin::new::<23>()); + plugins.push(Plugin::new::<24>()); + plugins.push(Plugin::new::<25>()); + plugins.push(Plugin::new::<26>()); + plugins.push(Plugin::new::<27>()); + plugins.push(Plugin::new::<28>()); + plugins.push(Plugin::new::<29>()); + plugins.push(Plugin::new::<30>()); + plugins.push(Plugin::new::<31>()); + plugins.push(Plugin::new::<32>()); + plugins.push(Plugin::new::<33>()); + plugins.push(Plugin::new::<34>()); + plugins.push(Plugin::new::<35>()); + plugins.push(Plugin::new::<36>()); + plugins.push(Plugin::new::<37>()); + plugins.push(Plugin::new::<38>()); + plugins.push(Plugin::new::<39>()); + plugins.push(Plugin::new::<40>()); + plugins.push(Plugin::new::<41>()); + plugins.push(Plugin::new::<42>()); + plugins.push(Plugin::new::<43>()); + plugins.push(Plugin::new::<44>()); + plugins.push(Plugin::new::<45>()); + plugins.push(Plugin::new::<46>()); + plugins.push(Plugin::new::<47>()); + plugins.push(Plugin::new::<48>()); + plugins.push(Plugin::new::<49>()); + plugins.push(Plugin::new::<50>()); + plugins.push(Plugin::new::<51>()); + plugins.push(Plugin::new::<52>()); + plugins.push(Plugin::new::<53>()); + plugins.push(Plugin::new::<54>()); + plugins.push(Plugin::new::<55>()); + plugins.push(Plugin::new::<56>()); + plugins.push(Plugin::new::<57>()); + plugins.push(Plugin::new::<58>()); + plugins.push(Plugin::new::<59>()); + plugins.push(Plugin::new::<60>()); + plugins.push(Plugin::new::<61>()); + plugins.push(Plugin::new::<62>()); + plugins.push(Plugin::new::<63>()); + plugins.push(Plugin::new::<64>()); + plugins.push(Plugin::new::<65>()); + plugins.push(Plugin::new::<66>()); + plugins.push(Plugin::new::<67>()); + plugins.push(Plugin::new::<68>()); + plugins.push(Plugin::new::<69>()); + plugins.push(Plugin::new::<70>()); + plugins.push(Plugin::new::<71>()); + plugins.push(Plugin::new::<72>()); + plugins.push(Plugin::new::<73>()); + plugins.push(Plugin::new::<74>()); + plugins.push(Plugin::new::<75>()); + plugins.push(Plugin::new::<76>()); + plugins.push(Plugin::new::<77>()); + plugins.push(Plugin::new::<78>()); + plugins.push(Plugin::new::<79>()); + plugins.push(Plugin::new::<80>()); + plugins.push(Plugin::new::<81>()); + plugins.push(Plugin::new::<82>()); + plugins.push(Plugin::new::<83>()); + plugins.push(Plugin::new::<84>()); + plugins.push(Plugin::new::<85>()); + plugins.push(Plugin::new::<86>()); + plugins.push(Plugin::new::<87>()); + plugins.push(Plugin::new::<88>()); + plugins.push(Plugin::new::<89>()); + plugins.push(Plugin::new::<90>()); + plugins.push(Plugin::new::<91>()); + plugins.push(Plugin::new::<92>()); + plugins.push(Plugin::new::<93>()); + plugins.push(Plugin::new::<94>()); + plugins.push(Plugin::new::<95>()); + plugins.push(Plugin::new::<96>()); + plugins.push(Plugin::new::<97>()); + plugins.push(Plugin::new::<98>()); + plugins.push(Plugin::new::<99>()); + Experiment::new(plugins) + }, + |experiment| { + let mut stage = SystemStage::parallel(); + experiment.write_to(&mut stage); + stage.run(&mut world); + }, + criterion::BatchSize::SmallInput, + ); + }); + group.finish(); } From 4deec3a3369e85e5fdf72ca1b206dce04d867abc Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 02:05:34 -0400 Subject: [PATCH 2/8] add more comments --- benches/benches/bevy_ecs/scheduling/schedule.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 97036b2936e07..2166bd1a82a99 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -113,6 +113,7 @@ pub fn build_schedule(criterion: &mut Criterion) { PubLabel::::ReallyReallySuperLongName.as_label(), ]; + // Initialize a list of systems with unique types. let systems = [ my_system::, my_system::, @@ -206,10 +207,12 @@ pub fn build_schedule(criterion: &mut Criterion) { let mut rng = rand::thread_rng(); + // Form inter-plugin dependencies for i in 0..plugins.len() { let (before, after) = plugins.split_at_mut(i); let (plugin, after) = after.split_first_mut().unwrap(); + // Have a chance to form a dependency with plugins coming before this one for other in before.iter() { if rng.next_u32() % 100 < PLUGIN_DEP_CHANCE { for system in &mut plugin.systems { @@ -219,6 +222,7 @@ pub fn build_schedule(criterion: &mut Criterion) { } } } + // Have a chance to form a dependency with plugins coming after this one for other in after.iter() { if rng.next_u32() % 100 < PLUGIN_DEP_CHANCE { for system in &mut plugin.systems { @@ -229,6 +233,8 @@ pub fn build_schedule(criterion: &mut Criterion) { } } + // Have a chance for every system in the plugin to form a dependency + // with every public label from every other plugin. for system in &mut plugin.systems { for &other_label in before.iter().flat_map(|other| &other.pub_labels) { if rng.next_u32() % 100 < OUTER_DEP_CHANCE { From 63a5cea7ef6abb3aa8acd581634a4b66baef847c Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 12:03:46 -0400 Subject: [PATCH 3/8] shrink source code with macros --- .../benches/bevy_ecs/scheduling/schedule.rs | 256 +++--------------- 1 file changed, 40 insertions(+), 216 deletions(-) diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 2166bd1a82a99..87ae4630e218e 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -114,67 +114,38 @@ pub fn build_schedule(criterion: &mut Criterion) { ]; // Initialize a list of systems with unique types. - let systems = [ - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, - my_system::, + macro_rules! declare_systems { + ($($J:literal),*) => { + [$(my_system::),*] + }; + } + let systems = declare_systems![ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]; + + // apply the plugin's label to each system. let systems = systems.map(|s| s.label(plugin_label)); let mut rng = rand::thread_rng(); let mut i = 0; let systems = systems.map(|mut system| { + // have a chance to form a dependency with every other system in this plugin. macro_rules! maybe_dep { - ($P:ident, $J:literal) => { + ($J:literal) => { if i != $J && rng.next_u32() % 100 < INNER_DEP_CHANCE { if i < $J { - system = system.before(my_system::<$P, $J>); + system = system.before(my_system::); } else { - system = system.after(my_system::<$P, $J>); + system = system.after(my_system::); } } }; + ($($J:literal),*) => { + $(maybe_dep!($J);)* + } } - - // have a chance to form a dependency with every other system in this plugin. - maybe_dep!(I, 0); - maybe_dep!(I, 1); - maybe_dep!(I, 2); - maybe_dep!(I, 3); - maybe_dep!(I, 4); - maybe_dep!(I, 5); - maybe_dep!(I, 6); - maybe_dep!(I, 7); - maybe_dep!(I, 8); - maybe_dep!(I, 9); - maybe_dep!(I, 10); - maybe_dep!(I, 11); - maybe_dep!(I, 12); - maybe_dep!(I, 13); - maybe_dep!(I, 14); - maybe_dep!(I, 15); - maybe_dep!(I, 16); - maybe_dep!(I, 17); - maybe_dep!(I, 18); - maybe_dep!(I, 19); + maybe_dep!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19); // have a chance to add public labels. for &label in &pub_labels { @@ -268,24 +239,20 @@ pub fn build_schedule(criterion: &mut Criterion) { group.warm_up_time(std::time::Duration::from_millis(500)); group.measurement_time(std::time::Duration::from_secs(15)); + macro_rules! experiment { + ($($N:literal),* $(,)?) => {{ + // this runs outside of the benchmark so we don't need to worry about `Vec::with_capacity`. + let mut plugins = Vec::new(); + // these must be pushed one by one to avoid overflowing the stack. + $( plugins.push(Plugin::new::<$N>()) ;)* + Experiment::new(plugins) + }} + } + group.bench_function("schedule 10 plugins", |bencher| { let mut world = World::new(); bencher.iter_batched( - || { - // these must be pushed one by one to avoid overflowing the stack. - let mut plugins = Vec::with_capacity(10); - plugins.push(Plugin::new::<0>()); - plugins.push(Plugin::new::<1>()); - plugins.push(Plugin::new::<2>()); - plugins.push(Plugin::new::<3>()); - plugins.push(Plugin::new::<4>()); - plugins.push(Plugin::new::<5>()); - plugins.push(Plugin::new::<6>()); - plugins.push(Plugin::new::<7>()); - plugins.push(Plugin::new::<8>()); - plugins.push(Plugin::new::<9>()); - Experiment::new(plugins) - }, + || experiment!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), |experiment| { let mut stage = SystemStage::parallel(); experiment.write_to(&mut stage); @@ -299,59 +266,11 @@ pub fn build_schedule(criterion: &mut Criterion) { let mut world = World::new(); bencher.iter_batched( || { - let mut plugins = Vec::with_capacity(10); - plugins.push(Plugin::new::<0>()); - plugins.push(Plugin::new::<1>()); - plugins.push(Plugin::new::<2>()); - plugins.push(Plugin::new::<3>()); - plugins.push(Plugin::new::<4>()); - plugins.push(Plugin::new::<5>()); - plugins.push(Plugin::new::<6>()); - plugins.push(Plugin::new::<7>()); - plugins.push(Plugin::new::<8>()); - plugins.push(Plugin::new::<9>()); - plugins.push(Plugin::new::<10>()); - plugins.push(Plugin::new::<11>()); - plugins.push(Plugin::new::<12>()); - plugins.push(Plugin::new::<13>()); - plugins.push(Plugin::new::<14>()); - plugins.push(Plugin::new::<15>()); - plugins.push(Plugin::new::<16>()); - plugins.push(Plugin::new::<17>()); - plugins.push(Plugin::new::<18>()); - plugins.push(Plugin::new::<19>()); - plugins.push(Plugin::new::<20>()); - plugins.push(Plugin::new::<21>()); - plugins.push(Plugin::new::<22>()); - plugins.push(Plugin::new::<23>()); - plugins.push(Plugin::new::<24>()); - plugins.push(Plugin::new::<25>()); - plugins.push(Plugin::new::<26>()); - plugins.push(Plugin::new::<27>()); - plugins.push(Plugin::new::<28>()); - plugins.push(Plugin::new::<29>()); - plugins.push(Plugin::new::<30>()); - plugins.push(Plugin::new::<31>()); - plugins.push(Plugin::new::<32>()); - plugins.push(Plugin::new::<33>()); - plugins.push(Plugin::new::<34>()); - plugins.push(Plugin::new::<35>()); - plugins.push(Plugin::new::<36>()); - plugins.push(Plugin::new::<37>()); - plugins.push(Plugin::new::<38>()); - plugins.push(Plugin::new::<39>()); - plugins.push(Plugin::new::<40>()); - plugins.push(Plugin::new::<41>()); - plugins.push(Plugin::new::<42>()); - plugins.push(Plugin::new::<43>()); - plugins.push(Plugin::new::<44>()); - plugins.push(Plugin::new::<45>()); - plugins.push(Plugin::new::<46>()); - plugins.push(Plugin::new::<47>()); - plugins.push(Plugin::new::<48>()); - plugins.push(Plugin::new::<49>()); - plugins.push(Plugin::new::<50>()); - Experiment::new(plugins) + experiment!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + ) }, |experiment| { let mut stage = SystemStage::parallel(); @@ -366,108 +285,13 @@ pub fn build_schedule(criterion: &mut Criterion) { let mut world = World::new(); bencher.iter_batched( || { - let mut plugins = Vec::with_capacity(10); - plugins.push(Plugin::new::<0>()); - plugins.push(Plugin::new::<1>()); - plugins.push(Plugin::new::<2>()); - plugins.push(Plugin::new::<3>()); - plugins.push(Plugin::new::<4>()); - plugins.push(Plugin::new::<5>()); - plugins.push(Plugin::new::<6>()); - plugins.push(Plugin::new::<7>()); - plugins.push(Plugin::new::<8>()); - plugins.push(Plugin::new::<9>()); - plugins.push(Plugin::new::<10>()); - plugins.push(Plugin::new::<11>()); - plugins.push(Plugin::new::<12>()); - plugins.push(Plugin::new::<13>()); - plugins.push(Plugin::new::<14>()); - plugins.push(Plugin::new::<15>()); - plugins.push(Plugin::new::<16>()); - plugins.push(Plugin::new::<17>()); - plugins.push(Plugin::new::<18>()); - plugins.push(Plugin::new::<19>()); - plugins.push(Plugin::new::<20>()); - plugins.push(Plugin::new::<21>()); - plugins.push(Plugin::new::<22>()); - plugins.push(Plugin::new::<23>()); - plugins.push(Plugin::new::<24>()); - plugins.push(Plugin::new::<25>()); - plugins.push(Plugin::new::<26>()); - plugins.push(Plugin::new::<27>()); - plugins.push(Plugin::new::<28>()); - plugins.push(Plugin::new::<29>()); - plugins.push(Plugin::new::<30>()); - plugins.push(Plugin::new::<31>()); - plugins.push(Plugin::new::<32>()); - plugins.push(Plugin::new::<33>()); - plugins.push(Plugin::new::<34>()); - plugins.push(Plugin::new::<35>()); - plugins.push(Plugin::new::<36>()); - plugins.push(Plugin::new::<37>()); - plugins.push(Plugin::new::<38>()); - plugins.push(Plugin::new::<39>()); - plugins.push(Plugin::new::<40>()); - plugins.push(Plugin::new::<41>()); - plugins.push(Plugin::new::<42>()); - plugins.push(Plugin::new::<43>()); - plugins.push(Plugin::new::<44>()); - plugins.push(Plugin::new::<45>()); - plugins.push(Plugin::new::<46>()); - plugins.push(Plugin::new::<47>()); - plugins.push(Plugin::new::<48>()); - plugins.push(Plugin::new::<49>()); - plugins.push(Plugin::new::<50>()); - plugins.push(Plugin::new::<51>()); - plugins.push(Plugin::new::<52>()); - plugins.push(Plugin::new::<53>()); - plugins.push(Plugin::new::<54>()); - plugins.push(Plugin::new::<55>()); - plugins.push(Plugin::new::<56>()); - plugins.push(Plugin::new::<57>()); - plugins.push(Plugin::new::<58>()); - plugins.push(Plugin::new::<59>()); - plugins.push(Plugin::new::<60>()); - plugins.push(Plugin::new::<61>()); - plugins.push(Plugin::new::<62>()); - plugins.push(Plugin::new::<63>()); - plugins.push(Plugin::new::<64>()); - plugins.push(Plugin::new::<65>()); - plugins.push(Plugin::new::<66>()); - plugins.push(Plugin::new::<67>()); - plugins.push(Plugin::new::<68>()); - plugins.push(Plugin::new::<69>()); - plugins.push(Plugin::new::<70>()); - plugins.push(Plugin::new::<71>()); - plugins.push(Plugin::new::<72>()); - plugins.push(Plugin::new::<73>()); - plugins.push(Plugin::new::<74>()); - plugins.push(Plugin::new::<75>()); - plugins.push(Plugin::new::<76>()); - plugins.push(Plugin::new::<77>()); - plugins.push(Plugin::new::<78>()); - plugins.push(Plugin::new::<79>()); - plugins.push(Plugin::new::<80>()); - plugins.push(Plugin::new::<81>()); - plugins.push(Plugin::new::<82>()); - plugins.push(Plugin::new::<83>()); - plugins.push(Plugin::new::<84>()); - plugins.push(Plugin::new::<85>()); - plugins.push(Plugin::new::<86>()); - plugins.push(Plugin::new::<87>()); - plugins.push(Plugin::new::<88>()); - plugins.push(Plugin::new::<89>()); - plugins.push(Plugin::new::<90>()); - plugins.push(Plugin::new::<91>()); - plugins.push(Plugin::new::<92>()); - plugins.push(Plugin::new::<93>()); - plugins.push(Plugin::new::<94>()); - plugins.push(Plugin::new::<95>()); - plugins.push(Plugin::new::<96>()); - plugins.push(Plugin::new::<97>()); - plugins.push(Plugin::new::<98>()); - plugins.push(Plugin::new::<99>()); - Experiment::new(plugins) + experiment!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + ) }, |experiment| { let mut stage = SystemStage::parallel(); From 39ac622ccda4d4f0b1a1c2bc5a8bf05641b8888e Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 12:33:49 -0400 Subject: [PATCH 4/8] use fixed-seed RNG --- benches/Cargo.toml | 2 +- benches/benches/bevy_ecs/scheduling/schedule.rs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 165fbce1cb25e..3e7773adee594 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dev-dependencies] glam = "0.21" -rand = "0.8" +rand = { version = "0.8", features = ["small_rng"] } rand_chacha = "0.3" criterion = { version = "0.3", features = ["html_reports"] } bevy_app = { path = "../crates/bevy_app" } diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 87ae4630e218e..3ac0ed1b1208a 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -103,7 +103,7 @@ pub fn build_schedule(criterion: &mut Criterion) { const OUTER_DEP_CHANCE: u32 = 10; impl Plugin { - fn new() -> Self { + fn new(rng: &mut impl RngCore) -> Self { let plugin_label = PluginLabel::.as_label(); let pub_labels = [ @@ -126,8 +126,6 @@ pub fn build_schedule(criterion: &mut Criterion) { // apply the plugin's label to each system. let systems = systems.map(|s| s.label(plugin_label)); - let mut rng = rand::thread_rng(); - let mut i = 0; let systems = systems.map(|mut system| { // have a chance to form a dependency with every other system in this plugin. @@ -173,11 +171,9 @@ pub fn build_schedule(criterion: &mut Criterion) { } impl Experiment { - fn new(plugins: impl IntoIterator) -> Self { + fn new(plugins: impl IntoIterator, rng: &mut impl RngCore) -> Self { let mut plugins: Vec<_> = plugins.into_iter().collect(); - let mut rng = rand::thread_rng(); - // Form inter-plugin dependencies for i in 0..plugins.len() { let (before, after) = plugins.split_at_mut(i); @@ -239,13 +235,16 @@ pub fn build_schedule(criterion: &mut Criterion) { group.warm_up_time(std::time::Duration::from_millis(500)); group.measurement_time(std::time::Duration::from_secs(15)); + use rand::SeedableRng; + let mut rng = rand::rngs::SmallRng::seed_from_u64(5410); + macro_rules! experiment { ($($N:literal),* $(,)?) => {{ // this runs outside of the benchmark so we don't need to worry about `Vec::with_capacity`. let mut plugins = Vec::new(); // these must be pushed one by one to avoid overflowing the stack. - $( plugins.push(Plugin::new::<$N>()) ;)* - Experiment::new(plugins) + $( plugins.push(Plugin::new::<$N>(&mut rng)) ;)* + Experiment::new(plugins, &mut rng) }} } From 3ab56c3eb58f58a89ba3cc8feed2a8905eb169bd Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 12:53:45 -0400 Subject: [PATCH 5/8] fix compile errors --- benches/benches/bevy_ecs/scheduling/schedule.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 3ac0ed1b1208a..70dddb8a46b38 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -1,6 +1,6 @@ use bevy_ecs::prelude::*; use criterion::Criterion; -use rand::RngCore; +use rand::{rngs::SmallRng, RngCore}; pub fn schedule(c: &mut Criterion) { #[derive(Component)] @@ -103,7 +103,7 @@ pub fn build_schedule(criterion: &mut Criterion) { const OUTER_DEP_CHANCE: u32 = 10; impl Plugin { - fn new(rng: &mut impl RngCore) -> Self { + fn new(rng: &mut SmallRng) -> Self { let plugin_label = PluginLabel::.as_label(); let pub_labels = [ @@ -171,7 +171,7 @@ pub fn build_schedule(criterion: &mut Criterion) { } impl Experiment { - fn new(plugins: impl IntoIterator, rng: &mut impl RngCore) -> Self { + fn new(plugins: impl IntoIterator, rng: &mut SmallRng) -> Self { let mut plugins: Vec<_> = plugins.into_iter().collect(); // Form inter-plugin dependencies @@ -236,7 +236,7 @@ pub fn build_schedule(criterion: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(15)); use rand::SeedableRng; - let mut rng = rand::rngs::SmallRng::seed_from_u64(5410); + let mut rng = SmallRng::seed_from_u64(5410); macro_rules! experiment { ($($N:literal),* $(,)?) => {{ From 8bf7503e83a205774176c3c7c744151ae7024e66 Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 13:45:04 -0400 Subject: [PATCH 6/8] don't time system-adding --- .../benches/bevy_ecs/scheduling/schedule.rs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 70dddb8a46b38..96aa724200b30 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -251,10 +251,12 @@ pub fn build_schedule(criterion: &mut Criterion) { group.bench_function("schedule 10 plugins", |bencher| { let mut world = World::new(); bencher.iter_batched( - || experiment!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), - |experiment| { + || { let mut stage = SystemStage::parallel(); - experiment.write_to(&mut stage); + experiment!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).write_to(&mut stage); + stage + }, + |mut stage| { stage.run(&mut world); }, criterion::BatchSize::SmallInput, @@ -265,15 +267,16 @@ pub fn build_schedule(criterion: &mut Criterion) { let mut world = World::new(); bencher.iter_batched( || { + let mut stage = SystemStage::parallel(); experiment!( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ) + .write_to(&mut stage); + stage }, - |experiment| { - let mut stage = SystemStage::parallel(); - experiment.write_to(&mut stage); + |mut stage| { stage.run(&mut world); }, criterion::BatchSize::SmallInput, @@ -284,6 +287,7 @@ pub fn build_schedule(criterion: &mut Criterion) { let mut world = World::new(); bencher.iter_batched( || { + let mut stage = SystemStage::parallel(); experiment!( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, @@ -291,10 +295,10 @@ pub fn build_schedule(criterion: &mut Criterion) { 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ) + .write_to(&mut stage); + stage }, - |experiment| { - let mut stage = SystemStage::parallel(); - experiment.write_to(&mut stage); + |mut stage| { stage.run(&mut world); }, criterion::BatchSize::SmallInput, From 298fa8690169fa7263457eff88ee0d1a84401937 Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 14:05:11 -0400 Subject: [PATCH 7/8] a word --- benches/benches/bevy_ecs/scheduling/schedule.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 96aa724200b30..803d171176925 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -57,7 +57,7 @@ pub fn schedule(c: &mut Criterion) { group.finish(); } -/// performs takes a value out of a reference, applies a fn, and puts it back in. +/// takes a value out of a reference, applies a fn, and puts it back in. /// stores a temporary dummy value while performing the operation. fn map_with_temp(ptr: &mut T, temp: T, f: impl FnOnce(T) -> T) { let val = std::mem::replace(ptr, temp); From 30084c278b5d612ee39d698074c9b1329dfbd16a Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Thu, 21 Jul 2022 14:05:22 -0400 Subject: [PATCH 8/8] Revert "don't time system-adding" This reverts commit 8bf7503e83a205774176c3c7c744151ae7024e66. --- .../benches/bevy_ecs/scheduling/schedule.rs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 803d171176925..18a35ebd393b6 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -251,12 +251,10 @@ pub fn build_schedule(criterion: &mut Criterion) { group.bench_function("schedule 10 plugins", |bencher| { let mut world = World::new(); bencher.iter_batched( - || { + || experiment!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), + |experiment| { let mut stage = SystemStage::parallel(); - experiment!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).write_to(&mut stage); - stage - }, - |mut stage| { + experiment.write_to(&mut stage); stage.run(&mut world); }, criterion::BatchSize::SmallInput, @@ -267,16 +265,15 @@ pub fn build_schedule(criterion: &mut Criterion) { let mut world = World::new(); bencher.iter_batched( || { - let mut stage = SystemStage::parallel(); experiment!( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ) - .write_to(&mut stage); - stage }, - |mut stage| { + |experiment| { + let mut stage = SystemStage::parallel(); + experiment.write_to(&mut stage); stage.run(&mut world); }, criterion::BatchSize::SmallInput, @@ -287,7 +284,6 @@ pub fn build_schedule(criterion: &mut Criterion) { let mut world = World::new(); bencher.iter_batched( || { - let mut stage = SystemStage::parallel(); experiment!( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, @@ -295,10 +291,10 @@ pub fn build_schedule(criterion: &mut Criterion) { 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ) - .write_to(&mut stage); - stage }, - |mut stage| { + |experiment| { + let mut stage = SystemStage::parallel(); + experiment.write_to(&mut stage); stage.run(&mut world); }, criterion::BatchSize::SmallInput,