-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Replace all labels with interned labels #7762
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
Conversation
|
Neat! I've added Controversial to all three of these PRs, to make sure the ECS SMEs consciously choose between them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've scanned over this quickly, and it definitely seems like a promising approach. Very similar to #7760 but potentially preferable.
Just for clarity, what are the differences between SystemSet::dyn_intern, Intern::intern, and IntoInterned::into_interned? Is it possible to simplify or combine any of these methods some way?
9d7efbf to
3b3b4e3
Compare
|
@JoJoJet I've replaced The functions that took I've also introduced a new trait and method |
3b3b4e3 to
35d535a
Compare
joseph-gio
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also switch AppLabel to use interned labels? It still uses pseudo-stringly typed labels, and I'd rather not leave that relic around.
|
@JoJoJet Regarding |
|
I guess that's fine, but is there a reason not to do it now? As far as I know, you'd just have to change a |
|
OK, after giving it a second thought, I've decided to do the I also changed |
|
Thank you for implementing my suggestions. I'll try to review this more closely today. |
|
I finally got around to this. I have a couple more ideas for how the API can be refined. Rather than going back-and-forth, I opened a PR to your branch: geieredgar#1. As always, feel free to voice any concerns you have with my suggestions. |
b9fb74f to
b91bee9
Compare
joseph-gio
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this PR is in a good state now! I just have a minor documentation suggestion.
|
It looks like your PR is a breaking change, but you didn't provide a migration guide. Could you add some context on what users should update when this change get released in a new version of Bevy? |
This can be removed from the cons list, and added to the pros. |
|
@nicopap After finding rust-lang/rust#106447 I think you are correct that this approach was unsound, because two equal labels could yield two different pointers. Therefore I removed the I also believe that two different labels cannot yield the same fat pointer when leaking, because then |
|
I've replaced the use of |
|
@geieredgar this is in the milestone for 0.12, slated for this Saturday. I'm going to do a full review now, but I'm hopeful we can get this in and ship it. Can you resolve merge conflicts? |
alice-i-cecile
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really cool stuff! The changes in schedule.rs and intern.rs are the only non-trivial changes: focus your review efforts there.
The breaking changes are acceptable and well-documented, the motivation is sound, I basically understand how this works, and this is incredibly well-tested. I'm comfortable approving this!
|
Conflicts are resolved. |
cart
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy with this approach!
crates/bevy_utils/src/intern.rs
Outdated
| } | ||
|
|
||
| fn ref_eq(&self, other: &Self) -> bool { | ||
| self.len() == other.len() && self.as_ptr() == other.as_ptr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an optimization, seems worth it to flip the order here to short-circuit on the ptr comparison? I think two interned strs are more likely to have different pointers than different lengths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just pushed as I doubt this will be controversial (and in the interest of moving forward).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave this open in case anyone wants to discuss.
# Objective First of all, this PR took heavy inspiration from bevyengine#7760 and bevyengine#5715. It intends to also fix bevyengine#5569, but with a slightly different approach. This also fixes bevyengine#9335 by reexporting `DynEq`. ## Solution The advantage of this API is that we can intern a value without allocating for zero-sized-types and for enum variants that have no fields. This PR does this automatically in the `SystemSet` and `ScheduleLabel` derive macros for unit structs and fieldless enum variants. So this should cover many internal and external use cases of `SystemSet` and `ScheduleLabel`. In these optimal use cases, no memory will be allocated. - The interning returns a `Interned<dyn SystemSet>`, which is just a wrapper around a `&'static dyn SystemSet`. - `Hash` and `Eq` are implemented in terms of the pointer value of the reference, similar to my first approach of anonymous system sets in bevyengine#7676. - Therefore, `Interned<T>` does not implement `Borrow<T>`, only `Deref`. - The debug output of `Interned<T>` is the same as the interned value. Edit: - `AppLabel` is now also interned and the old `derive_label`/`define_label` macros were replaced with the new interning implementation. - Anonymous set ids are reused for different `Schedule`s, reducing the amount of leaked memory. ### Pros - `InternedSystemSet` and `InternedScheduleLabel` behave very similar to the current `BoxedSystemSet` and `BoxedScheduleLabel`, but can be copied without an allocation. - Many use cases don't allocate at all. - Very fast lookups and comparisons when using `InternedSystemSet` and `InternedScheduleLabel`. - The `intern` module might be usable in other areas. - `Interned{ScheduleLabel, SystemSet, AppLabel}` does implement `{ScheduleLabel, SystemSet, AppLabel}`, increasing ergonomics. ### Cons - Implementors of `SystemSet` and `ScheduleLabel` still need to implement `Hash` and `Eq` (and `Clone`) for it to work. ## Changelog ### Added - Added `intern` module to `bevy_utils`. - Added reexports of `DynEq` to `bevy_ecs` and `bevy_app`. ### Changed - Replaced `BoxedSystemSet` and `BoxedScheduleLabel` with `InternedSystemSet` and `InternedScheduleLabel`. - Replaced `impl AsRef<dyn ScheduleLabel>` with `impl ScheduleLabel`. - Replaced `AppLabelId` with `InternedAppLabel`. - Changed `AppLabel` to use `Debug` for error messages. - Changed `AppLabel` to use interning. - Changed `define_label`/`derive_label` to use interning. - Replaced `define_boxed_label`/`derive_boxed_label` with `define_label`/`derive_label`. - Changed anonymous set ids to be only unique inside a schedule, not globally. - Made interned label types implement their label trait. ### Removed - Removed `define_boxed_label` and `derive_boxed_label`. ## Migration guide - Replace `BoxedScheduleLabel` and `Box<dyn ScheduleLabel>` with `InternedScheduleLabel` or `Interned<dyn ScheduleLabel>`. - Replace `BoxedSystemSet` and `Box<dyn SystemSet>` with `InternedSystemSet` or `Interned<dyn SystemSet>`. - Replace `AppLabelId` with `InternedAppLabel` or `Interned<dyn AppLabel>`. - Types manually implementing `ScheduleLabel`, `AppLabel` or `SystemSet` need to implement: - `dyn_hash` directly instead of implementing `DynHash` - `as_dyn_eq` - Pass labels to `World::try_schedule_scope`, `World::schedule_scope`, `World::try_run_schedule`. `World::run_schedule`, `Schedules::remove`, `Schedules::remove_entry`, `Schedules::contains`, `Schedules::get` and `Schedules::get_mut` by value instead of by reference. --------- Co-authored-by: Joseph <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
# Objective First of all, this PR took heavy inspiration from bevyengine#7760 and bevyengine#5715. It intends to also fix bevyengine#5569, but with a slightly different approach. This also fixes bevyengine#9335 by reexporting `DynEq`. ## Solution The advantage of this API is that we can intern a value without allocating for zero-sized-types and for enum variants that have no fields. This PR does this automatically in the `SystemSet` and `ScheduleLabel` derive macros for unit structs and fieldless enum variants. So this should cover many internal and external use cases of `SystemSet` and `ScheduleLabel`. In these optimal use cases, no memory will be allocated. - The interning returns a `Interned<dyn SystemSet>`, which is just a wrapper around a `&'static dyn SystemSet`. - `Hash` and `Eq` are implemented in terms of the pointer value of the reference, similar to my first approach of anonymous system sets in bevyengine#7676. - Therefore, `Interned<T>` does not implement `Borrow<T>`, only `Deref`. - The debug output of `Interned<T>` is the same as the interned value. Edit: - `AppLabel` is now also interned and the old `derive_label`/`define_label` macros were replaced with the new interning implementation. - Anonymous set ids are reused for different `Schedule`s, reducing the amount of leaked memory. ### Pros - `InternedSystemSet` and `InternedScheduleLabel` behave very similar to the current `BoxedSystemSet` and `BoxedScheduleLabel`, but can be copied without an allocation. - Many use cases don't allocate at all. - Very fast lookups and comparisons when using `InternedSystemSet` and `InternedScheduleLabel`. - The `intern` module might be usable in other areas. - `Interned{ScheduleLabel, SystemSet, AppLabel}` does implement `{ScheduleLabel, SystemSet, AppLabel}`, increasing ergonomics. ### Cons - Implementors of `SystemSet` and `ScheduleLabel` still need to implement `Hash` and `Eq` (and `Clone`) for it to work. ## Changelog ### Added - Added `intern` module to `bevy_utils`. - Added reexports of `DynEq` to `bevy_ecs` and `bevy_app`. ### Changed - Replaced `BoxedSystemSet` and `BoxedScheduleLabel` with `InternedSystemSet` and `InternedScheduleLabel`. - Replaced `impl AsRef<dyn ScheduleLabel>` with `impl ScheduleLabel`. - Replaced `AppLabelId` with `InternedAppLabel`. - Changed `AppLabel` to use `Debug` for error messages. - Changed `AppLabel` to use interning. - Changed `define_label`/`derive_label` to use interning. - Replaced `define_boxed_label`/`derive_boxed_label` with `define_label`/`derive_label`. - Changed anonymous set ids to be only unique inside a schedule, not globally. - Made interned label types implement their label trait. ### Removed - Removed `define_boxed_label` and `derive_boxed_label`. ## Migration guide - Replace `BoxedScheduleLabel` and `Box<dyn ScheduleLabel>` with `InternedScheduleLabel` or `Interned<dyn ScheduleLabel>`. - Replace `BoxedSystemSet` and `Box<dyn SystemSet>` with `InternedSystemSet` or `Interned<dyn SystemSet>`. - Replace `AppLabelId` with `InternedAppLabel` or `Interned<dyn AppLabel>`. - Types manually implementing `ScheduleLabel`, `AppLabel` or `SystemSet` need to implement: - `dyn_hash` directly instead of implementing `DynHash` - `as_dyn_eq` - Pass labels to `World::try_schedule_scope`, `World::schedule_scope`, `World::try_run_schedule`. `World::run_schedule`, `Schedules::remove`, `Schedules::remove_entry`, `Schedules::contains`, `Schedules::get` and `Schedules::get_mut` by value instead of by reference. --------- Co-authored-by: Joseph <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
Objective
First of all, this PR took heavy inspiration from #7760 and #5715. It intends to also fix #5569, but with a slightly different approach.
This also fixes #9335 by reexporting
DynEq.Solution
The advantage of this API is that we can intern a value without allocating for zero-sized-types and for enum variants that have no fields. This PR does this automatically in the
SystemSetandScheduleLabelderive macros for unit structs and fieldless enum variants. So this should cover many internal and external use cases ofSystemSetandScheduleLabel. In these optimal use cases, no memory will be allocated.Interned<dyn SystemSet>, which is just a wrapper around a&'static dyn SystemSet.HashandEqare implemented in terms of the pointer value of the reference, similar to my first approach of anonymous system sets inrun_ifforSystemConfigsvia anonymous system sets #7676.Interned<T>does not implementBorrow<T>, onlyDeref.Interned<T>is the same as the interned value.Edit:
AppLabelis now also interned and the oldderive_label/define_labelmacros were replaced with the new interning implementation.Schedules, reducing the amount of leaked memory.Pros
InternedSystemSetandInternedScheduleLabelbehave very similar to the currentBoxedSystemSetandBoxedScheduleLabel, but can be copied without an allocation.InternedSystemSetandInternedScheduleLabel.internmodule might be usable in other areas.Interned{ScheduleLabel, SystemSet, AppLabel}does implement{ScheduleLabel, SystemSet, AppLabel}, increasing ergonomics.Cons
SystemSetandScheduleLabelstill need to implementHashandEq(andClone) for it to work.Changelog
Added
internmodule tobevy_utils.DynEqtobevy_ecsandbevy_app.Changed
BoxedSystemSetandBoxedScheduleLabelwithInternedSystemSetandInternedScheduleLabel.impl AsRef<dyn ScheduleLabel>withimpl ScheduleLabel.AppLabelIdwithInternedAppLabel.AppLabelto useDebugfor error messages.AppLabelto use interning.define_label/derive_labelto use interning.define_boxed_label/derive_boxed_labelwithdefine_label/derive_label.Removed
define_boxed_labelandderive_boxed_label.Migration guide
BoxedScheduleLabelandBox<dyn ScheduleLabel>withInternedScheduleLabelorInterned<dyn ScheduleLabel>.BoxedSystemSetandBox<dyn SystemSet>withInternedSystemSetorInterned<dyn SystemSet>.AppLabelIdwithInternedAppLabelorInterned<dyn AppLabel>.ScheduleLabel,AppLabelorSystemSetneed to implement:dyn_hashdirectly instead of implementingDynHashas_dyn_eqWorld::try_schedule_scope,World::schedule_scope,World::try_run_schedule.World::run_schedule,Schedules::remove,Schedules::remove_entry,Schedules::contains,Schedules::getandSchedules::get_mutby value instead of by reference.