Skip to content

Stop storing access for all systems #19477

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 13, 2025

Conversation

chescock
Copy link
Contributor

@chescock chescock commented Jun 3, 2025

Objective

Reduce memory usage by storing fewer copies of FilteredAccessSet<ComponentId>.

Currently, the System trait exposes the component_access_set for the system, which is used by the multi-threaded executor to determine which systems can run concurrently. But because it is available on the trait, it needs to be stored for every system, even ones that are not run by the executor! In particular, it is never needed for observers, or for the inner systems in a PipeSystem or CombinatorSystem.

Solution

Instead of exposing the access from a method on System, return it from System::initialize. Since it is still needed during scheduling, store the access alongside the boxed system in the schedule.

That's not quite enough for systems built using SystemParamBuilders, though. Those calculate the access in SystemParamBuilder::build, which happens earlier than System::initialize. To handle those, we separate SystemParam::init_state into init_state, which creates the state value, and init_access, which calculates the access. This lets System::initialize call init_access on a state that was provided by the builder.

An additional benefit of that separation is that it removes the need to duplicate access checks between SystemParamBuilder::build and SystemParam::init_state.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Performance A change motivated by improving speed, memory usage or compile times C-Code-Quality A section of code that is hard to understand or change S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 3, 2025
Copy link
Contributor

@re0312 re0312 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely stellar implementation! The code is remarkably clean and well-structured.

Copy link
Contributor

@Trashtalk217 Trashtalk217 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like the system.system and condition.condition stuttering with the SystemWithAccess and the ConditionWithAccess struct, but in general, I think this is a really good change. If it passes CI, I'll approve it.

type Target = str;
fn deref(&self) -> &Self::Target {
self.name()
}
}

// SAFETY: no component value access
unsafe impl SystemParam for SystemName<'_> {
type State = Cow<'static, str>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this? I'm curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this? I'm curious.

Oh, right, I meant to leave a note about that!

I removed the SystemMeta parameter from init_state since I thought it would only be used by the parts that had moved to init_access. This turned out to be the one place that actually initialized the state from the SystemMeta! Note that for most systems, name is a Cow::Borrowed, so this won't be any slower.

I can restore the SystemMeta parameter if anyone is opposed to this change. We might want to do something else to ensure that existing calls to init_state fail to compile, though, since users will need to change those to add calls to init_access.

I think the ideal solution here would be to let get_param borrow from SystemMeta. That would also avoid needing to clone() it in ParamSet. But that would touch a lot of code for a fairly minor performance improvement, especially since this PR already saves having to clone the access.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this seems good. Thanks for the explanation.

…cess-ii

# Conflicts:
#	crates/bevy_ecs/src/schedule/executor/mod.rs
#	crates/bevy_ecs/src/system/adapter_system.rs
#	crates/bevy_ecs/src/system/combinator.rs
#	crates/bevy_ecs/src/system/exclusive_function_system.rs
#	crates/bevy_ecs/src/system/function_system.rs
#	crates/bevy_ecs/src/system/observer_system.rs
#	crates/bevy_ecs/src/system/schedule_system.rs
#	crates/bevy_ecs/src/system/system.rs
chescock added 3 commits June 9, 2025 20:18
…cess-ii

# Conflicts:
#	crates/bevy_ecs/src/schedule/executor/mod.rs
#	crates/bevy_ecs/src/schedule/schedule.rs
#	crates/bevy_ecs/src/system/adapter_system.rs
#	crates/bevy_ecs/src/system/combinator.rs
#	crates/bevy_ecs/src/system/exclusive_function_system.rs
#	crates/bevy_ecs/src/system/function_system.rs
#	crates/bevy_ecs/src/system/mod.rs
#	crates/bevy_ecs/src/system/observer_system.rs
#	crates/bevy_ecs/src/system/schedule_system.rs
#	crates/bevy_ecs/src/system/system.rs
#	crates/bevy_ecs/src/world/unsafe_world_cell.rs
Update PR number in migration guide.
…cess-ii

# Conflicts:
#	crates/bevy_ecs/src/removal_detection.rs
@Trashtalk217 Trashtalk217 added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 13, 2025
Copy link
Contributor

@urben1680 urben1680 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done, I like this! Getting rid of both accesses in System will also make things easier for my manual impls. 😄

@alice-i-cecile alice-i-cecile enabled auto-merge June 13, 2025 17:34
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Jun 13, 2025
Merged via the queue into bevyengine:main with commit bb4ea9c Jun 13, 2025
32 checks passed
hukasu pushed a commit to hukasu/bevy that referenced this pull request Jun 15, 2025
# Objective

Reduce memory usage by storing fewer copies of
`FilteredAccessSet<ComponentId>`.

Currently, the `System` trait exposes the `component_access_set` for the
system, which is used by the multi-threaded executor to determine which
systems can run concurrently. But because it is available on the trait,
it needs to be stored for *every* system, even ones that are not run by
the executor! In particular, it is never needed for observers, or for
the inner systems in a `PipeSystem` or `CombinatorSystem`.


## Solution

Instead of exposing the access from a method on `System`, return it from
`System::initialize`. Since it is still needed during scheduling, store
the access alongside the boxed system in the schedule.

That's not quite enough for systems built using `SystemParamBuilder`s,
though. Those calculate the access in `SystemParamBuilder::build`, which
happens earlier than `System::initialize`. To handle those, we separate
`SystemParam::init_state` into `init_state`, which creates the state
value, and `init_access`, which calculates the access. This lets
`System::initialize` call `init_access` on a state that was provided by
the builder.

An additional benefit of that separation is that it removes the need to
duplicate access checks between `SystemParamBuilder::build` and
`SystemParam::init_state`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change C-Performance A change motivated by improving speed, memory usage or compile times S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants