Skip to content

Commit

Permalink
feat(system::iter): add par_entities_with
Browse files Browse the repository at this point in the history
  • Loading branch information
SOF3 committed Oct 21, 2023
1 parent 197bbcd commit ff287f3
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 134 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ itertools = "0.10.3"
log = "0.4.16"
parking_lot = {version = "0.12.0", features = ["owning_ref", "arc_lock", "send_guard"]}
rand = "0.8.5"
rayon = "1.5.2"
rayon = "1.8.0"
static_assertions = "1.1.0"
strum = {version = "0.24.0", optional = true}
xias = "0.3.0"
Expand All @@ -47,6 +47,7 @@ internal-bench = ["env_logger", "strum", "tuple-impl-8-zip"] # Internal feature:
criterion = { version = "0.4.0", features = ["html_reports"] }
env_logger = "0.10.0"
lazy_static = "1.4.0"
paste = "1.0.14"
strum = "0.24.0"

[lib]
Expand Down
107 changes: 43 additions & 64 deletions benches/iter_entity_add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,44 +32,6 @@ fn individual_comps(rng: &mut ThreadRng) -> comp::Map<TestArch> {
]
}

#[system]
fn system_individual_add_system_non_chunked(
mut px: system::WriteSimple<TestArch, PositionX>,
mut py: system::WriteSimple<TestArch, PositionY>,
mut pz: system::WriteSimple<TestArch, PositionZ>,
vx: system::ReadSimple<TestArch, VelocityX>,
vy: system::ReadSimple<TestArch, VelocityY>,
vz: system::ReadSimple<TestArch, VelocityZ>,
entities: system::EntityIterator<TestArch>,
) {
for (_, (px, py, pz, vx, vy, vz)) in
entities.entities_with((&mut px, &mut py, &mut pz, &vx, &vy, &vz))
{
px.0 += vx.0;
py.0 += vy.0;
pz.0 += vz.0;
}
}

#[system]
fn system_individual_add_system_chunked(
mut px: system::WriteSimple<TestArch, PositionX>,
mut py: system::WriteSimple<TestArch, PositionY>,
mut pz: system::WriteSimple<TestArch, PositionZ>,
vx: system::ReadSimple<TestArch, VelocityX>,
vy: system::ReadSimple<TestArch, VelocityY>,
vz: system::ReadSimple<TestArch, VelocityZ>,
entities: system::EntityIterator<TestArch>,
) {
entities.entities_with_chunked((&mut px, &mut py, &mut pz, &vx, &vy, &vz)).for_each(
|(_, (px, py, pz, vx, vy, vz))| {
px.0 += vx.0;
py.0 += vy.0;
pz.0 += vz.0;
},
)
}

#[dynec::comp(of = TestArch, required)]
struct PositionArray([f64; 3]);
#[dynec::comp(of = TestArch, required)]
Expand All @@ -90,32 +52,49 @@ fn array_comps(rng: &mut ThreadRng) -> comp::Map<TestArch> {
]
}

#[system]
fn system_array_add_system_non_chunked(
mut p: system::WriteSimple<TestArch, PositionArray>,
v: system::ReadSimple<TestArch, VelocityArray>,
entities: system::EntityIterator<TestArch>,
) {
for (_, (p, v)) in entities.entities_with((&mut p, &v)) {
for i in 0..3 {
p.0[i] += v.0[i];
}
}
}
macro_rules! make_systems {
($system_name:ident $iter_method:ident) => {
paste::paste! {
#[system]
fn [<$system_name _idv>](
mut px: system::WriteSimple<TestArch, PositionX>,
mut py: system::WriteSimple<TestArch, PositionY>,
mut pz: system::WriteSimple<TestArch, PositionZ>,
vx: system::ReadSimple<TestArch, VelocityX>,
vy: system::ReadSimple<TestArch, VelocityY>,
vz: system::ReadSimple<TestArch, VelocityZ>,
entities: system::EntityIterator<TestArch>,
) {
entities.$iter_method((&mut px, &mut py, &mut pz, &vx, &vy, &vz)).for_each(
|(_, (px, py, pz, vx, vy, vz))| {
px.0 += vx.0;
py.0 += vy.0;
pz.0 += vz.0;
},
)
}

#[system]
fn system_array_add_system_chunked(
mut p: system::WriteSimple<TestArch, PositionArray>,
v: system::ReadSimple<TestArch, VelocityArray>,
entities: system::EntityIterator<TestArch>,
) {
for (_, (p, v)) in entities.entities_with_chunked((&mut p, &v)) {
for i in 0..3 {
p.0[i] += v.0[i];
#[system]
fn [<$system_name _arr>](
mut p: system::WriteSimple<TestArch, PositionArray>,
v: system::ReadSimple<TestArch, VelocityArray>,
entities: system::EntityIterator<TestArch>,
) {
entities.$iter_method((&mut p, &v)).for_each(
|(_, (p, v))| {
for i in 0..3 {
p.0[i] += v.0[i];
}
},
)
}
}
}
};
}

make_systems!(system_add_ent entities_with);
make_systems!(system_add_chunk entities_with_chunked);

fn bench_iter_entity_add<SystemT, DeleteEntityIter>(
group: &mut BenchmarkGroup<'_, measurement::WallTime>,
subgroup: &str,
Expand Down Expand Up @@ -160,31 +139,31 @@ fn iter_entity_add_with_deletion<DeleteEntityIter: Iterator<Item = u64>>(
group,
name,
"ent idv",
|| system_individual_add_system_non_chunked.build(),
|| system_add_ent_idv.build(),
individual_comps,
deletion,
);
bench_iter_entity_add(
group,
name,
"chunk idv",
|| system_individual_add_system_chunked.build(),
|| system_add_chunk_idv.build(),
individual_comps,
deletion,
);
bench_iter_entity_add(
group,
name,
"ent arr",
|| system_array_add_system_non_chunked.build(),
|| system_add_ent_arr.build(),
array_comps,
deletion,
);
bench_iter_entity_add(
group,
name,
"chunk arr",
|| system_array_add_system_chunked.build(),
|| system_add_chunk_arr.build(),
array_comps,
deletion,
);
Expand Down
4 changes: 2 additions & 2 deletions src/entity/ealloc/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl<E: Raw> Snapshot<E> {
}
}

#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub(crate) struct Slice<'t, E> {
pub(crate) start: E,
pub(crate) end: E,
Expand All @@ -46,7 +46,7 @@ impl<'t, E: Raw> Slice<'t, E> {
// For now, we just take the assumption that the holes are uniformly distributed.

let midpt = self.start.approx_midpoint(self.end);
let is_far = self.end.sub(midpt) < 8;
let is_far = self.end.sub(midpt) >= 8;
is_far.then_some(midpt)
}

Expand Down
75 changes: 74 additions & 1 deletion src/system/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
use std::marker::PhantomData;
use std::{any, iter, mem, ops};

use rayon::prelude::ParallelIterator;

use super::access::single;
use crate::entity::ealloc::snapshot;
use crate::entity::{ealloc, Raw as _};
use crate::system::access;
use crate::{comp, entity, storage, util, Archetype, Storage};
Expand Down Expand Up @@ -97,6 +100,76 @@ impl<A: Archetype> EntityIterator<A> {
)
})
}

fn par_raw_chunks<IntoZ: IntoZip<A>>(
&self,
zip: IntoZ,
) -> impl ParallelIterator<Item = (snapshot::Slice<'_, A::RawEntity>, IntoZ::IntoZip)>
where
IntoZ::IntoZip: Send,
{
rayon::iter::split((self.ealloc.as_slice(), zip.into_zip()), |(slice, zip)| {
let Some(midpt) = slice.midpoint_for_split() else { return ((slice, zip), None) };
let (slice_left, slice_right) = slice.split_at(midpt);
let mut zip_left = zip;
let zip_right = zip_left.split(midpt);
((slice_left, zip_left), Some((slice_right, zip_right)))
})
}

/// Iterates over all entities in parallel, yielding the components requested.
pub fn par_entities_with<IntoZ: IntoZip<A>>(
&self,
zip: IntoZ,
) -> impl ParallelIterator<Item = (entity::TempRef<A>, <IntoZ::IntoZip as Zip<A>>::Item)>
where
IntoZ::IntoZip: Send,
<IntoZ::IntoZip as Zip<A>>::Item: Send,
{
self.par_raw_chunks(zip).flat_map_iter(|(slice, zip)| {
let mut zip_iter = ZipIter(zip, PhantomData);
entity::Raw::range(slice.start..slice.end)
.map(move |entity| (entity::TempRef::new(entity), zip_iter.take_serial(entity)))
})
}

/// Iterates over all chunks of entities in parallel, yielding the components requested.
pub fn par_chunks_with<IntoZ: IntoZip<A>>(
&self,
zip: IntoZ,
) -> impl ParallelIterator<Item = (entity::TempRefChunk<A>, <IntoZ::IntoZip as ZipChunked<A>>::Chunk)>
where
IntoZ::IntoZip: ZipChunked<A> + Send,
<IntoZ::IntoZip as ZipChunked<A>>::Chunk: Send,
{
self.par_raw_chunks(zip).map(|(slice, zip)| {
let mut zip_iter = ZipIter(zip, PhantomData);
(
entity::TempRefChunk::new(slice.start, slice.end),
zip_iter.take_serial_chunk(slice.start, slice.end),
)
})
}

/// Same as [`par_entities_with`](Self::par_entities_with),
/// but leverages chunked storages for better performance.
pub fn par_entities_with_chunked<IntoZ: IntoZip<A>>(
&self,
zip: IntoZ,
) -> impl ParallelIterator<Item = (entity::TempRef<A>, <IntoZ::IntoZip as Zip<A>>::Item)>
where
IntoZ::IntoZip: ZipChunked<A> + Send,
<IntoZ::IntoZip as Zip<A>>::Item: Send,
{
self.par_raw_chunks(zip).flat_map_iter(|(slice, zip)| {
iter::zip(
entity::Raw::range(slice.start..slice.end).map(entity::TempRef::new),
<IntoZ::IntoZip as ZipChunked<A>>::chunk_to_entities(
ZipIter(zip, PhantomData).take_serial_chunk(slice.start, slice.end),
),
)
})
}
}

struct ZipIter<A: Archetype, Z: Zip<A>>(Z, PhantomData<A>);
Expand Down Expand Up @@ -169,7 +242,7 @@ pub trait IntoZip<A: Archetype> {
}

/// Determines how to resolve the case of a missing Result.
pub trait MissingResln {
pub trait MissingResln: Send + Sync {
/// The return type of the resolution.
type Result<T>;
/// Resolves an optional value.
Expand Down
Loading

0 comments on commit ff287f3

Please sign in to comment.