Skip to content
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

Removed once_cell #10079

Merged
merged 2 commits into from
Oct 12, 2023
Merged

Conversation

bushrat011899
Copy link
Contributor

@bushrat011899 bushrat011899 commented Oct 10, 2023

Objective

Solution

  • Replaced 1 instance of OnceBox<T> with OnceLock<T> in NonGenericTypeCell

Notes

All changes are in the private side of Bevy, and appear to have no observable change in performance or compilation time. This is purely to reduce the quantity of direct dependencies in Bevy.

Replaced 1 instance of `OnceBox<T>` with `OnceLock<Box<T>>` in `NonGenericTypeCell`
@alice-i-cecile alice-i-cecile added C-Dependencies A change to the crates that Bevy depends on C-Code-Quality A section of code that is hard to understand or change labels Oct 10, 2023
@alice-i-cecile alice-i-cecile requested a review from MrGVSV October 10, 2023 23:22
@MrGVSV MrGVSV added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Oct 10, 2023
Copy link
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

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

It's not clear this is 100% equivalent since the once_cell version allows racing to initialize, while the std implementation is unclear what its behavior is, or if it uses a lock underneath or not. These come with semantic and potential performance differences depending on the underlying implementation.

Since this seems to be a fairly foundational type within bevy_reflect, I'd defer to the reflection SMEs to make a final call on whether this ambiguity is acceptable or not.

@bushrat011899
Copy link
Contributor Author

Since this seems to be a fairly foundational type within bevy_reflect, I'd defer to the reflection SMEs to make a final call on whether this ambiguity is acceptable or not.

Perfectly reasonable. Comparing OnceLock::get_or_init and OnceBox::get_or_init, the key difference is whether it races to completion (fastest thread wins), or blocks until completion (first thread wins).

Based on the handful of locations I can see the NonGenericTypeCell::get_or_set being used, they all appear to have "constant" parameters:

// Snip from bevy_asset/src/path.rs

impl Typed for AssetPath<'static> {
    fn type_info() -> &'static TypeInfo {
        static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
        CELL.get_or_set(|| {
            let info = ValueInfo::new::<Self>();
            TypeInfo::Value(info)
        })
//     ^--------------------------------------^
//       This will always produce the same value
    }
}

So the value won't change moving from race to block behaviour, leaving timing as the only other possible change. Again, all the calls to NonGenericTypeCell::get_or_set have identical methods and parameters. As such, I think the blocking behaviour will perform slightly better than race, as only one copy of the initializer will ever be run, instead of multiple threads running it simultaneously. I don't think that performance change could ever be observed, since these functions are so short.

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label Oct 11, 2023
@SkiFire13
Copy link
Contributor

Note that the race can happen only once. Moreover they are very likely to be initialized at the start of the program, when an App is created and the types are registered in the TypeRegistry. Thus there's likely no race anyway.

If anything, I would remove the Box. It was needed in OnceBox only because there's no way to race the initialization of arbitrary types, but it's very easy to do so when the type involved is represented as a Box. Thus if we don't need the race we can just remove the Box and save a bunch of allocations.

@bushrat011899
Copy link
Contributor Author

If anything, I would remove the Box. It was needed in OnceBox only because there's no way to race the initialization of arbitrary types, but it's very easy to do so when the type involved is represented as a Box. Thus if we don't need the race we can just remove the Box and save a bunch of allocations.

Just got some time to test that, and removing the closure and Box in NonGenericTypeCell::get_or_set appears to make no difference, so I've removed them for the sake of simplicity. I don't have any benchmarking setup to measure a performance difference, but just playing some of the examples I couldn't notice anything different. Personally, I think removing it makes that code a tiny bit cleaner. Thanks for the suggestion!

@james7132
Copy link
Member

james7132 commented Oct 12, 2023

Checking the usage patterns a bit more deeply, I'm inclined to agree that this shouldn't be too big of an issue outside of building the type registry. Still unfortunate that we need this at all since this does seem to be necessary only because there's no const constructor for TypeId or type_name.

@bushrat011899
Copy link
Contributor Author

Still unfortunate that we need this at all since this does seem to be necessary only because there's no const constructor for TypeId or type_name.

Completely agree. I was looking at the TypePath trait just earlier thinking it would be great to make this all const. It seems a lot of code could start becoming const if we had access to a const type_name...

@alice-i-cecile alice-i-cecile removed the X-Controversial There is active debate or serious implications around merging this PR label Oct 12, 2023
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Oct 12, 2023
Merged via the queue into bevyengine:main with commit bb13d06 Oct 12, 2023
24 of 25 checks passed
regnarock pushed a commit to regnarock/bevy that referenced this pull request Oct 13, 2023
# Objective

- Fixes bevyengine#8303

## Solution

- Replaced 1 instance of `OnceBox<T>` with `OnceLock<T>` in
`NonGenericTypeCell`

## Notes

All changes are in the private side of Bevy, and appear to have no
observable change in performance or compilation time. This is purely to
reduce the quantity of direct dependencies in Bevy.
ameknite pushed a commit to ameknite/bevy that referenced this pull request Nov 6, 2023
# Objective

- Fixes bevyengine#8303

## Solution

- Replaced 1 instance of `OnceBox<T>` with `OnceLock<T>` in
`NonGenericTypeCell`

## Notes

All changes are in the private side of Bevy, and appear to have no
observable change in performance or compilation time. This is purely to
reduce the quantity of direct dependencies in Bevy.
rdrpenguin04 pushed a commit to rdrpenguin04/bevy that referenced this pull request Jan 9, 2024
# Objective

- Fixes bevyengine#8303

## Solution

- Replaced 1 instance of `OnceBox<T>` with `OnceLock<T>` in
`NonGenericTypeCell`

## Notes

All changes are in the private side of Bevy, and appear to have no
observable change in performance or compilation time. This is purely to
reduce the quantity of direct dependencies in Bevy.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-Code-Quality A section of code that is hard to understand or change C-Dependencies A change to the crates that Bevy depends on 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.

Replace once_cell with std lib's implementation
5 participants