From 02c59eb80bf67c525b83396ef7d6a8c1d250d409 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 11 May 2022 12:50:34 -0700 Subject: [PATCH 01/12] Skip empty archetypes and tables when iterating over queries --- crates/bevy_ecs/src/query/iter.rs | 8 ++++++++ crates/bevy_ecs/src/query/state.rs | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 25bc9720b2984..5de70efc0b406 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -85,6 +85,10 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &self.tables[*table_id]; + if table.is_empty() { + continue; + } + self.fetch.set_table(&self.query_state.fetch_state, table); self.filter.set_table(&self.query_state.filter_state, table); self.current_len = table.len(); @@ -107,6 +111,10 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &self.archetypes[*archetype_id]; + if archetype.is_empty() { + continue; + } + self.fetch.set_archetype( &self.query_state.fetch_state, archetype, diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index e01d8df5f2b35..8ae6389bf2872 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -794,6 +794,10 @@ impl QueryState { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; + if table.is_empty() { + continue; + } + fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); @@ -810,6 +814,10 @@ impl QueryState { let tables = &world.storages().tables; for archetype_id in &self.matched_archetype_ids { let archetype = &archetypes[*archetype_id]; + if archetype.is_empty() { + continue; + } + fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); @@ -853,6 +861,9 @@ impl QueryState { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; + if table.is_empty() { + continue; + } let mut offset = 0; while offset < table.len() { let func = func.clone(); @@ -886,6 +897,9 @@ impl QueryState { for archetype_id in &self.matched_archetype_ids { let mut offset = 0; let archetype = &archetypes[*archetype_id]; + if archetype.is_empty() { + continue; + } while offset < archetype.len() { let func = func.clone(); scope.spawn(async move { From 43b9a659de3821f17397f8d598656e9f2a07be1c Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 11 May 2022 13:20:00 -0700 Subject: [PATCH 02/12] Update QueryIterationCursor --- crates/bevy_ecs/src/query/iter.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 5de70efc0b406..adff0a655f401 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -442,6 +442,10 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &tables[*table_id]; + if table.is_empty() { + continue; + } + self.fetch.set_table(&query_state.fetch_state, table); self.filter.set_table(&query_state.filter_state, table); self.current_len = table.len(); @@ -464,6 +468,10 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; + if archetpe.is_empty() { + continue; + } + self.fetch .set_archetype(&query_state.fetch_state, archetype, tables); self.filter From 29da7b9a300ca77c0463d0c039faee6d96d33d9e Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 11 May 2022 14:17:07 -0700 Subject: [PATCH 03/12] Remove unnecessary continues --- crates/bevy_ecs/src/query/iter.rs | 4 ---- crates/bevy_ecs/src/query/state.rs | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index adff0a655f401..267570c280040 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -93,7 +93,6 @@ where self.filter.set_table(&self.query_state.filter_state, table); self.current_len = table.len(); self.current_index = 0; - continue; } if !self.filter.table_filter_fetch(self.current_index) { @@ -127,7 +126,6 @@ where ); self.current_len = archetype.len(); self.current_index = 0; - continue; } if !self.filter.archetype_filter_fetch(self.current_index) { @@ -450,7 +448,6 @@ where self.filter.set_table(&query_state.filter_state, table); self.current_len = table.len(); self.current_index = 0; - continue; } if !self.filter.table_filter_fetch(self.current_index) { @@ -478,7 +475,6 @@ where .set_archetype(&query_state.filter_state, archetype, tables); self.current_len = archetype.len(); self.current_index = 0; - continue; } if !self.filter.archetype_filter_fetch(self.current_index) { diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 8ae6389bf2872..3010a05e6a66a 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -864,6 +864,7 @@ impl QueryState { if table.is_empty() { continue; } + let mut offset = 0; while offset < table.len() { let func = func.clone(); @@ -900,6 +901,7 @@ impl QueryState { if archetype.is_empty() { continue; } + while offset < archetype.len() { let func = func.clone(); scope.spawn(async move { From 10d03b933831460df3bbc75c009faad1017fc1a4 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 11 May 2022 14:25:40 -0700 Subject: [PATCH 04/12] Fix typo --- crates/bevy_ecs/src/query/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 267570c280040..5f8ec8247bf6d 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -465,7 +465,7 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; - if archetpe.is_empty() { + if archetype.is_empty() { continue; } From fe5859a262b5322176d138db3253a2ce2f08b9b4 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 11 May 2022 20:19:53 -0700 Subject: [PATCH 05/12] Add benchmark --- benches/Cargo.toml | 5 + benches/benches/bevy_ecs/empty_archetypes.rs | 156 +++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 benches/benches/bevy_ecs/empty_archetypes.rs diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 4c8e51c280348..4dbcb5008c0f9 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -19,6 +19,11 @@ name = "archetype_updates" path = "benches/bevy_ecs/archetype_updates.rs" harness = false +[[bench]] +name = "empty_archetypes" +path = "benches/bevy_ecs/empty_archetypes.rs" +harness = false + [[bench]] name = "ecs_bench_suite" path = "benches/bevy_ecs/ecs_bench_suite/mod.rs" diff --git a/benches/benches/bevy_ecs/empty_archetypes.rs b/benches/benches/bevy_ecs/empty_archetypes.rs new file mode 100644 index 0000000000000..98b22fa9296bc --- /dev/null +++ b/benches/benches/bevy_ecs/empty_archetypes.rs @@ -0,0 +1,156 @@ +use bevy_ecs::{ + component::Component, + prelude::*, + schedule::{Stage, SystemStage}, + world::World, +}; +use bevy_tasks::{ComputeTaskPool, TaskPool}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; + +criterion_group!(benches, empty_archetypes); +criterion_main!(benches); + +#[derive(Component)] +struct A(f32); + +fn iter(query: Query<&A<0>>) { + for comp in query.iter() { + black_box(comp); + } +} + +fn for_each(query: Query<&A<0>>) { + query.for_each(|comp| { + black_box(comp); + }); +} + +fn par_for_each(task_pool: Res, query: Query<&A<0>>) { + query.par_for_each(&*task_pool, 64, |comp| { + black_box(comp); + }); +} + +fn setup(parallel: bool, setup: impl FnOnce(&mut SystemStage)) -> (World, SystemStage) { + let mut world = World::new(); + let mut stage = SystemStage::parallel(); + if parallel { + world.insert_resource(ComputeTaskPool(TaskPool::default())); + } + setup(&mut stage); + stage.run(&mut world); + (world, stage) +} + +/// create `count` entities with distinct archetypes +fn add_archetypes(world: &mut World, count: u16) { + for i in 0..count { + let mut e = world.spawn(); + if i & 1 << 0 != 0 { + e.insert(A::<0>(1.0)); + } + if i & 1 << 1 != 0 { + e.insert(A::<1>(1.0)); + } + if i & 1 << 2 != 0 { + e.insert(A::<2>(1.0)); + } + if i & 1 << 3 != 0 { + e.insert(A::<3>(1.0)); + } + if i & 1 << 4 != 0 { + e.insert(A::<4>(1.0)); + } + if i & 1 << 5 != 0 { + e.insert(A::<5>(1.0)); + } + if i & 1 << 6 != 0 { + e.insert(A::<6>(1.0)); + } + if i & 1 << 7 != 0 { + e.insert(A::<7>(1.0)); + } + if i & 1 << 8 != 0 { + e.insert(A::<8>(1.0)); + } + if i & 1 << 9 != 0 { + e.insert(A::<9>(1.0)); + } + if i & 1 << 10 != 0 { + e.insert(A::<10>(1.0)); + } + if i & 1 << 11 != 0 { + e.insert(A::<11>(1.0)); + } + if i & 1 << 12 != 0 { + e.insert(A::<12>(1.0)); + } + if i & 1 << 13 != 0 { + e.insert(A::<13>(1.0)); + } + if i & 1 << 14 != 0 { + e.insert(A::<14>(1.0)); + } + if i & 1 << 15 != 0 { + e.insert(A::<15>(1.0)); + } + } +} + +fn empty_archetypes(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("empty_archetypes"); + for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] { + let (mut world, mut stage) = setup(true, |stage| { + stage.add_system(iter); + }); + add_archetypes(&mut world, archetype_count); + world.clear_entities(); + let mut e = world.spawn(); + e.insert(A::<0>(1.0)); + group.bench_with_input( + BenchmarkId::new("iter", archetype_count), + &archetype_count, + |bencher, &_| { + bencher.iter(|| { + stage.run(&mut world); + }) + }, + ); + } + for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] { + let (mut world, mut stage) = setup(true, |stage| { + stage.add_system(for_each); + }); + add_archetypes(&mut world, archetype_count); + world.clear_entities(); + let mut e = world.spawn(); + e.insert(A::<0>(1.0)); + group.bench_with_input( + BenchmarkId::new("for_each", archetype_count), + &archetype_count, + |bencher, &_| { + bencher.iter(|| { + stage.run(&mut world); + }) + }, + ); + } + for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] { + let (mut world, mut stage) = setup(true, |stage| { + stage.add_system(par_for_each); + }); + add_archetypes(&mut world, archetype_count); + world.clear_entities(); + let mut e = world.spawn(); + e.insert(A::<0>(1.0)); + group.bench_with_input( + BenchmarkId::new("par_for_each", archetype_count), + &archetype_count, + |bencher, &_| { + bencher.iter(|| { + stage.run(&mut world); + }) + }, + ); + } +} From 7fe737924836ca55b9b253945e0ff9d0ea03ae64 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 11 May 2022 20:32:31 -0700 Subject: [PATCH 06/12] Fix up erroneous benchmark --- benches/benches/bevy_ecs/empty_archetypes.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benches/benches/bevy_ecs/empty_archetypes.rs b/benches/benches/bevy_ecs/empty_archetypes.rs index 98b22fa9296bc..0a459324cfa28 100644 --- a/benches/benches/bevy_ecs/empty_archetypes.rs +++ b/benches/benches/bevy_ecs/empty_archetypes.rs @@ -38,7 +38,6 @@ fn setup(parallel: bool, setup: impl FnOnce(&mut SystemStage)) -> (World, System world.insert_resource(ComputeTaskPool(TaskPool::default())); } setup(&mut stage); - stage.run(&mut world); (world, stage) } @@ -46,9 +45,7 @@ fn setup(parallel: bool, setup: impl FnOnce(&mut SystemStage)) -> (World, System fn add_archetypes(world: &mut World, count: u16) { for i in 0..count { let mut e = world.spawn(); - if i & 1 << 0 != 0 { - e.insert(A::<0>(1.0)); - } + e.insert(A::<0>(1.0)); if i & 1 << 1 != 0 { e.insert(A::<1>(1.0)); } @@ -107,6 +104,7 @@ fn empty_archetypes(criterion: &mut Criterion) { world.clear_entities(); let mut e = world.spawn(); e.insert(A::<0>(1.0)); + stage.run(&mut world); group.bench_with_input( BenchmarkId::new("iter", archetype_count), &archetype_count, @@ -125,6 +123,7 @@ fn empty_archetypes(criterion: &mut Criterion) { world.clear_entities(); let mut e = world.spawn(); e.insert(A::<0>(1.0)); + stage.run(&mut world); group.bench_with_input( BenchmarkId::new("for_each", archetype_count), &archetype_count, @@ -143,6 +142,7 @@ fn empty_archetypes(criterion: &mut Criterion) { world.clear_entities(); let mut e = world.spawn(); e.insert(A::<0>(1.0)); + stage.run(&mut world); group.bench_with_input( BenchmarkId::new("par_for_each", archetype_count), &archetype_count, From 4815ff5fa8dfcf450a670c9f761814b12a355434 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 13 May 2022 23:28:06 -0700 Subject: [PATCH 07/12] Use wider queries --- benches/benches/bevy_ecs/empty_archetypes.rs | 133 ++++++++++++++++--- 1 file changed, 115 insertions(+), 18 deletions(-) diff --git a/benches/benches/bevy_ecs/empty_archetypes.rs b/benches/benches/bevy_ecs/empty_archetypes.rs index 0a459324cfa28..0db82700e691e 100644 --- a/benches/benches/bevy_ecs/empty_archetypes.rs +++ b/benches/benches/bevy_ecs/empty_archetypes.rs @@ -13,19 +13,68 @@ criterion_main!(benches); #[derive(Component)] struct A(f32); -fn iter(query: Query<&A<0>>) { +fn iter( + query: Query<( + &A<0>, + &A<1>, + &A<2>, + &A<3>, + &A<4>, + &A<5>, + &A<6>, + &A<7>, + &A<8>, + &A<9>, + &A<10>, + &A<11>, + &A<12>, + )>, +) { for comp in query.iter() { black_box(comp); } } -fn for_each(query: Query<&A<0>>) { +fn for_each( + query: Query<( + &A<0>, + &A<1>, + &A<2>, + &A<3>, + &A<4>, + &A<5>, + &A<6>, + &A<7>, + &A<8>, + &A<9>, + &A<10>, + &A<11>, + &A<12>, + )>, +) { query.for_each(|comp| { black_box(comp); }); } -fn par_for_each(task_pool: Res, query: Query<&A<0>>) { +fn par_for_each( + task_pool: Res, + query: Query<( + &A<0>, + &A<1>, + &A<2>, + &A<3>, + &A<4>, + &A<5>, + &A<6>, + &A<7>, + &A<8>, + &A<9>, + &A<10>, + &A<11>, + &A<12>, + )>, +) { query.par_for_each(&*task_pool, 64, |comp| { black_box(comp); }); @@ -46,50 +95,62 @@ fn add_archetypes(world: &mut World, count: u16) { for i in 0..count { let mut e = world.spawn(); e.insert(A::<0>(1.0)); + e.insert(A::<1>(1.0)); + e.insert(A::<2>(1.0)); + e.insert(A::<3>(1.0)); + e.insert(A::<4>(1.0)); + e.insert(A::<5>(1.0)); + e.insert(A::<6>(1.0)); + e.insert(A::<7>(1.0)); + e.insert(A::<8>(1.0)); + e.insert(A::<9>(1.0)); + e.insert(A::<10>(1.0)); + e.insert(A::<11>(1.0)); + e.insert(A::<12>(1.0)); if i & 1 << 1 != 0 { - e.insert(A::<1>(1.0)); + e.insert(A::<13>(1.0)); } if i & 1 << 2 != 0 { - e.insert(A::<2>(1.0)); + e.insert(A::<14>(1.0)); } if i & 1 << 3 != 0 { - e.insert(A::<3>(1.0)); + e.insert(A::<15>(1.0)); } if i & 1 << 4 != 0 { - e.insert(A::<4>(1.0)); + e.insert(A::<16>(1.0)); } if i & 1 << 5 != 0 { - e.insert(A::<5>(1.0)); + e.insert(A::<18>(1.0)); } if i & 1 << 6 != 0 { - e.insert(A::<6>(1.0)); + e.insert(A::<19>(1.0)); } if i & 1 << 7 != 0 { - e.insert(A::<7>(1.0)); + e.insert(A::<20>(1.0)); } if i & 1 << 8 != 0 { - e.insert(A::<8>(1.0)); + e.insert(A::<21>(1.0)); } if i & 1 << 9 != 0 { - e.insert(A::<9>(1.0)); + e.insert(A::<22>(1.0)); } if i & 1 << 10 != 0 { - e.insert(A::<10>(1.0)); + e.insert(A::<23>(1.0)); } if i & 1 << 11 != 0 { - e.insert(A::<11>(1.0)); + e.insert(A::<24>(1.0)); } if i & 1 << 12 != 0 { - e.insert(A::<12>(1.0)); + e.insert(A::<25>(1.0)); } if i & 1 << 13 != 0 { - e.insert(A::<13>(1.0)); + e.insert(A::<26>(1.0)); } if i & 1 << 14 != 0 { - e.insert(A::<14>(1.0)); + e.insert(A::<27>(1.0)); } if i & 1 << 15 != 0 { - e.insert(A::<15>(1.0)); + e.insert(A::<28>(1.0)); } } } @@ -104,6 +165,18 @@ fn empty_archetypes(criterion: &mut Criterion) { world.clear_entities(); let mut e = world.spawn(); e.insert(A::<0>(1.0)); + e.insert(A::<1>(1.0)); + e.insert(A::<2>(1.0)); + e.insert(A::<3>(1.0)); + e.insert(A::<4>(1.0)); + e.insert(A::<5>(1.0)); + e.insert(A::<6>(1.0)); + e.insert(A::<7>(1.0)); + e.insert(A::<8>(1.0)); + e.insert(A::<9>(1.0)); + e.insert(A::<10>(1.0)); + e.insert(A::<11>(1.0)); + e.insert(A::<12>(1.0)); stage.run(&mut world); group.bench_with_input( BenchmarkId::new("iter", archetype_count), @@ -123,6 +196,18 @@ fn empty_archetypes(criterion: &mut Criterion) { world.clear_entities(); let mut e = world.spawn(); e.insert(A::<0>(1.0)); + e.insert(A::<1>(1.0)); + e.insert(A::<2>(1.0)); + e.insert(A::<3>(1.0)); + e.insert(A::<4>(1.0)); + e.insert(A::<5>(1.0)); + e.insert(A::<6>(1.0)); + e.insert(A::<7>(1.0)); + e.insert(A::<8>(1.0)); + e.insert(A::<9>(1.0)); + e.insert(A::<10>(1.0)); + e.insert(A::<11>(1.0)); + e.insert(A::<12>(1.0)); stage.run(&mut world); group.bench_with_input( BenchmarkId::new("for_each", archetype_count), @@ -142,6 +227,18 @@ fn empty_archetypes(criterion: &mut Criterion) { world.clear_entities(); let mut e = world.spawn(); e.insert(A::<0>(1.0)); + e.insert(A::<1>(1.0)); + e.insert(A::<2>(1.0)); + e.insert(A::<3>(1.0)); + e.insert(A::<4>(1.0)); + e.insert(A::<5>(1.0)); + e.insert(A::<6>(1.0)); + e.insert(A::<7>(1.0)); + e.insert(A::<8>(1.0)); + e.insert(A::<9>(1.0)); + e.insert(A::<10>(1.0)); + e.insert(A::<11>(1.0)); + e.insert(A::<12>(1.0)); stage.run(&mut world); group.bench_with_input( BenchmarkId::new("par_for_each", archetype_count), From 72884a9e182b8560d7ab5bc257726c5955ad355f Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 14 May 2022 00:41:51 -0700 Subject: [PATCH 08/12] Move checks to update_archetypes --- crates/bevy_ecs/src/query/iter.rs | 24 ++++------------ crates/bevy_ecs/src/query/state.rs | 46 +++++++++++++++++------------- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 5f8ec8247bf6d..b190e157ad130 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -60,8 +60,8 @@ where archetypes: &world.archetypes, fetch, filter, - table_id_iter: query_state.matched_table_ids.iter(), - archetype_id_iter: query_state.matched_archetype_ids.iter(), + table_id_iter: query_state.valid_table_ids.iter(), + archetype_id_iter: query_state.valid_archetype_ids.iter(), current_len: 0, current_index: 0, } @@ -85,10 +85,7 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &self.tables[*table_id]; - if table.is_empty() { - continue; - } - + // table_id_iter always produces non-empty tables self.fetch.set_table(&self.query_state.fetch_state, table); self.filter.set_table(&self.query_state.filter_state, table); self.current_len = table.len(); @@ -110,10 +107,7 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &self.archetypes[*archetype_id]; - if archetype.is_empty() { - continue; - } - + // archetype_id_iter always produces non-empty archetypes self.fetch.set_archetype( &self.query_state.fetch_state, archetype, @@ -440,10 +434,7 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &tables[*table_id]; - if table.is_empty() { - continue; - } - + // table_id_iter always produces non-empty tables self.fetch.set_table(&query_state.fetch_state, table); self.filter.set_table(&query_state.filter_state, table); self.current_len = table.len(); @@ -465,10 +456,7 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; - if archetype.is_empty() { - continue; - } - + // archetype_id_iter always produces non-empty tables self.fetch .set_archetype(&query_state.fetch_state, archetype, tables); self.filter diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 3010a05e6a66a..28f95c7b156e9 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -28,6 +28,8 @@ pub struct QueryState { pub(crate) matched_table_ids: Vec, // NOTE: we maintain both a ArchetypeId bitset and a vec because iterating the vec is faster pub(crate) matched_archetype_ids: Vec, + pub(super) valid_table_ids: Vec, + pub(super) valid_archetype_ids: Vec, pub(crate) fetch_state: Q::State, pub(crate) filter_state: F::State, } @@ -62,6 +64,8 @@ impl QueryState { archetype_generation: ArchetypeGeneration::initial(), matched_table_ids: Vec::new(), matched_archetype_ids: Vec::new(), + valid_table_ids: Vec::new(), + valid_archetype_ids: Vec::new(), fetch_state, filter_state, component_access, @@ -100,6 +104,24 @@ impl QueryState { for archetype_index in archetype_index_range { self.new_archetype(&archetypes[ArchetypeId::new(archetype_index)]); } + + let archetypes = &world.archetypes; + let tables = &world.storages().tables; + + self.valid_archetype_ids.clear(); + self.valid_archetype_ids.extend( + self.matched_archetype_ids + .iter() + .copied() + .filter(|id| !archetypes[*id].is_empty()), + ); + self.valid_table_ids.clear(); + self.valid_table_ids.extend( + self.matched_table_ids + .iter() + .copied() + .filter(|id| !tables[*id].is_empty()), + ); } #[inline] @@ -792,12 +814,8 @@ impl QueryState { if >::IS_DENSE && >::IS_DENSE { let tables = &world.storages().tables; - for table_id in &self.matched_table_ids { + for table_id in &self.valid_table_ids { let table = &tables[*table_id]; - if table.is_empty() { - continue; - } - fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); @@ -812,12 +830,8 @@ impl QueryState { } else { let archetypes = &world.archetypes; let tables = &world.storages().tables; - for archetype_id in &self.matched_archetype_ids { + for archetype_id in &self.valid_archetype_ids { let archetype = &archetypes[*archetype_id]; - if archetype.is_empty() { - continue; - } - fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); @@ -859,12 +873,8 @@ impl QueryState { task_pool.scope(|scope| { if QF::IS_DENSE && >::IS_DENSE { let tables = &world.storages().tables; - for table_id in &self.matched_table_ids { + for table_id in &self.valid_table_ids { let table = &tables[*table_id]; - if table.is_empty() { - continue; - } - let mut offset = 0; while offset < table.len() { let func = func.clone(); @@ -895,13 +905,9 @@ impl QueryState { } } else { let archetypes = &world.archetypes; - for archetype_id in &self.matched_archetype_ids { + for archetype_id in &self.valid_archetype_ids { let mut offset = 0; let archetype = &archetypes[*archetype_id]; - if archetype.is_empty() { - continue; - } - while offset < archetype.len() { let func = func.clone(); scope.spawn(async move { From 282f70e1c3b80f31d17403edf4fa78569ce984a6 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 14 May 2022 02:13:53 -0700 Subject: [PATCH 09/12] Revert "Move checks to update_archetypes" This reverts commit 72884a9e182b8560d7ab5bc257726c5955ad355f. --- crates/bevy_ecs/src/query/iter.rs | 24 ++++++++++++---- crates/bevy_ecs/src/query/state.rs | 46 +++++++++++++----------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index b190e157ad130..5f8ec8247bf6d 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -60,8 +60,8 @@ where archetypes: &world.archetypes, fetch, filter, - table_id_iter: query_state.valid_table_ids.iter(), - archetype_id_iter: query_state.valid_archetype_ids.iter(), + table_id_iter: query_state.matched_table_ids.iter(), + archetype_id_iter: query_state.matched_archetype_ids.iter(), current_len: 0, current_index: 0, } @@ -85,7 +85,10 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &self.tables[*table_id]; - // table_id_iter always produces non-empty tables + if table.is_empty() { + continue; + } + self.fetch.set_table(&self.query_state.fetch_state, table); self.filter.set_table(&self.query_state.filter_state, table); self.current_len = table.len(); @@ -107,7 +110,10 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &self.archetypes[*archetype_id]; - // archetype_id_iter always produces non-empty archetypes + if archetype.is_empty() { + continue; + } + self.fetch.set_archetype( &self.query_state.fetch_state, archetype, @@ -434,7 +440,10 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &tables[*table_id]; - // table_id_iter always produces non-empty tables + if table.is_empty() { + continue; + } + self.fetch.set_table(&query_state.fetch_state, table); self.filter.set_table(&query_state.filter_state, table); self.current_len = table.len(); @@ -456,7 +465,10 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; - // archetype_id_iter always produces non-empty tables + if archetype.is_empty() { + continue; + } + self.fetch .set_archetype(&query_state.fetch_state, archetype, tables); self.filter diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 277b98d7f1a75..ec888094b5aa1 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -30,8 +30,6 @@ pub struct QueryState { pub(crate) matched_table_ids: Vec, // NOTE: we maintain both a ArchetypeId bitset and a vec because iterating the vec is faster pub(crate) matched_archetype_ids: Vec, - pub(super) valid_table_ids: Vec, - pub(super) valid_archetype_ids: Vec, pub(crate) fetch_state: Q::State, pub(crate) filter_state: F::State, } @@ -66,8 +64,6 @@ impl QueryState { archetype_generation: ArchetypeGeneration::initial(), matched_table_ids: Vec::new(), matched_archetype_ids: Vec::new(), - valid_table_ids: Vec::new(), - valid_archetype_ids: Vec::new(), fetch_state, filter_state, component_access, @@ -106,24 +102,6 @@ impl QueryState { for archetype_index in archetype_index_range { self.new_archetype(&archetypes[ArchetypeId::new(archetype_index)]); } - - let archetypes = &world.archetypes; - let tables = &world.storages().tables; - - self.valid_archetype_ids.clear(); - self.valid_archetype_ids.extend( - self.matched_archetype_ids - .iter() - .copied() - .filter(|id| !archetypes[*id].is_empty()), - ); - self.valid_table_ids.clear(); - self.valid_table_ids.extend( - self.matched_table_ids - .iter() - .copied() - .filter(|id| !tables[*id].is_empty()), - ); } #[inline] @@ -816,8 +794,12 @@ impl QueryState { if >::IS_DENSE && >::IS_DENSE { let tables = &world.storages().tables; - for table_id in &self.valid_table_ids { + for table_id in &self.matched_table_ids { let table = &tables[*table_id]; + if table.is_empty() { + continue; + } + fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); @@ -832,8 +814,12 @@ impl QueryState { } else { let archetypes = &world.archetypes; let tables = &world.storages().tables; - for archetype_id in &self.valid_archetype_ids { + for archetype_id in &self.matched_archetype_ids { let archetype = &archetypes[*archetype_id]; + if archetype.is_empty() { + continue; + } + fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); @@ -875,8 +861,12 @@ impl QueryState { task_pool.scope(|scope| { if QF::IS_DENSE && >::IS_DENSE { let tables = &world.storages().tables; - for table_id in &self.valid_table_ids { + for table_id in &self.matched_table_ids { let table = &tables[*table_id]; + if table.is_empty() { + continue; + } + let mut offset = 0; while offset < table.len() { let func = func.clone(); @@ -917,9 +907,13 @@ impl QueryState { } } else { let archetypes = &world.archetypes; - for archetype_id in &self.valid_archetype_ids { + for archetype_id in &self.matched_archetype_ids { let mut offset = 0; let archetype = &archetypes[*archetype_id]; + if archetype.is_empty() { + continue; + } + while offset < archetype.len() { let func = func.clone(); let len = batch_size.min(archetype.len() - offset); From 37f5b97d4b117a5681a9a693710779bffb27f07f Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 29 Jun 2022 18:44:06 -0700 Subject: [PATCH 10/12] Revert for non-parallel cases --- crates/bevy_ecs/src/query/state.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index e2dd074feb834..ec74520d091b4 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -893,10 +893,6 @@ impl QueryState { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; - if table.is_empty() { - continue; - } - fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); @@ -913,10 +909,6 @@ impl QueryState { let tables = &world.storages().tables; for archetype_id in &self.matched_archetype_ids { let archetype = &archetypes[*archetype_id]; - if archetype.is_empty() { - continue; - } - fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); From 436e4741810a15e632aa5b609b6f47f382d142db Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 29 Jun 2022 18:44:59 -0700 Subject: [PATCH 11/12] Revert iter changes --- crates/bevy_ecs/src/query/iter.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 94af6eecb7953..b860f9cac6a4f 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -473,14 +473,12 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &tables[*table_id]; - if table.is_empty() { - continue; - } self.fetch.set_table(&query_state.fetch_state, table); self.filter.set_table(&query_state.filter_state, table); self.current_len = table.len(); self.current_index = 0; + continue; } if !self.filter.table_filter_fetch(self.current_index) { @@ -498,9 +496,6 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; - if archetype.is_empty() { - continue; - } self.fetch .set_archetype(&query_state.fetch_state, archetype, tables); @@ -508,6 +503,7 @@ where .set_archetype(&query_state.filter_state, archetype, tables); self.current_len = archetype.len(); self.current_index = 0; + continue; } if !self.filter.archetype_filter_fetch(self.current_index) { From f87f12c5d0b7e7c6b96d2b50054b231c2029baea Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 29 Jun 2022 18:45:31 -0700 Subject: [PATCH 12/12] Undo extra spaces --- crates/bevy_ecs/src/query/iter.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index b860f9cac6a4f..58c6d40b2a708 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -473,7 +473,6 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &tables[*table_id]; - self.fetch.set_table(&query_state.fetch_state, table); self.filter.set_table(&query_state.filter_state, table); self.current_len = table.len(); @@ -496,7 +495,6 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; - self.fetch .set_archetype(&query_state.fetch_state, archetype, tables); self.filter