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

[Merged by Bors] - reduce tricky unsafety and simplify table structure #2221

Closed
wants to merge 3 commits into from

Conversation

Frizi
Copy link
Contributor

@Frizi Frizi commented May 19, 2021

I've noticed that we are overusing interior mutability of the Table data, where in many cases we already own a unique reference to it. That prompted a slight refactor aiming to reduce number of safety constraints that must be manually upheld. Now the majority of those are just about avoiding bound checking, which is relatively easy to prove right.

Another aspect is reducing the complexity of Table struct. Notably, we don't ever use archetypes stored there, so this whole thing goes away. Capacity and grow amount were mostly superficial, as we are already using Vecs inside anyway, so I've got rid of those too. Now the overall table capacity is being driven by the internal entity Vec capacity. This has a side effect of automatically implementing exponential growth pattern for BitVecs reallocations inside Table, which to my measurements slightly improves performance in tests that are heavy on inserts. YMMV, but I hope that those tests were at least remotely correct.

@Frizi
Copy link
Contributor Author

Frizi commented May 19, 2021

For completeness, here are my benchmark results. Just note that my machine is fairly noisy.

group                                       main                     simplify-table
-----                                       ----                     --------------
add_remove_component/bevy_sparse_set        1.00  1387.4±108.00µs    1.00  1392.9±214.10µs
add_remove_component/bevy_table             1.02  1736.9±107.17µs    1.00  1703.5±138.67µs
add_remove_component_big/bevy_sparse_set    1.02  1570.9±309.53µs    1.00  1546.0±214.15µs
add_remove_component_big/bevy_table         1.10      4.1±0.19ms     1.00      3.7±0.07ms
fragmented_iter/bevy                        1.02   462.5±46.10ns     1.00   454.9±44.52ns
fragmented_iter/bevy_foreach                1.03   319.8±30.22ns     1.00   310.7±31.22ns
get_component/bevy                          1.00  1456.6±41.49µs     1.04  1513.2±216.42µs
get_component/bevy_system                   1.02  1235.0±135.91µs    1.00  1207.3±154.55µs
heavy_compute/bevy                          1.01  1057.7±21.91µs     1.00  1050.8±12.90µs
schedule/bevy                               1.00     59.7±3.03µs     1.00     59.4±3.15µs
simple_insert/bevy                          1.03   633.6±82.98µs     1.00   614.2±19.95µs
simple_insert/bevy_unbatched                1.11  1649.8±41.47µs     1.00  1492.0±37.32µs
simple_iter/bevy                            1.00     22.5±2.41µs     1.04     23.3±2.64µs
simple_iter/bevy_foreach                    1.00     12.5±0.50µs     1.01     12.6±1.09µs
simple_iter/bevy_sparse                     1.05     62.3±5.71µs     1.00     59.5±0.71µs
simple_iter/bevy_sparse_foreach             1.00    60.4±10.05µs     1.07    64.8±12.89µs
sparse_fragmented_iter/bevy                 1.00     15.1±1.13ns     1.02     15.4±1.45ns
sparse_fragmented_iter/bevy_foreach         1.05     13.4±1.19ns     1.00     12.7±0.30ns

@NathanSWard NathanSWard added A-ECS Entities, components, systems, and events C-Performance A change motivated by improving speed, memory usage or compile times labels May 19, 2021
Copy link
Contributor

@NathanSWard NathanSWard left a comment

Choose a reason for hiding this comment

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

Other than these little nits, this looks good! :)

crates/bevy_ecs/src/storage/table.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/storage/table.rs Show resolved Hide resolved
@NathanSWard NathanSWard 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 May 20, 2021
#[inline]
fn ticks_mut(&mut self) -> &mut Vec<ComponentTicks> {
// SAFE: unique access
unsafe { &mut *self.ticks.get() }
Copy link
Member

Choose a reason for hiding this comment

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

We can make this safe using self.ticks.get_mut()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh cool! I for some reason was unable to find this method. I see it's stabilised since 1.50.
Note that this UnsafeCell goes away in #2227 completely.

@cart
Copy link
Member

cart commented May 24, 2021

I am getting a reproducible simple_iter regression. From ~16.07 -> ~17.55 (and the results you posted also show a regression). But this doesn't even touch iteration code (maybe with the exception of tick access, and reverting that doesn't change anything). The for_each variants aren't affected, and I think we will get similar performance from normal query iterators if we adopt derive(Component). I'm personally cool with eating this regression for now.

@cart
Copy link
Member

cart commented May 24, 2021

bors r+

bors bot pushed a commit that referenced this pull request May 24, 2021
I've noticed that we are overusing interior mutability of the Table data, where in many cases we already own a unique reference to it. That prompted a slight refactor aiming to reduce number of safety constraints that must be manually upheld. Now the majority of those are just about avoiding bound checking, which is relatively easy to prove right.

Another aspect is reducing the complexity of Table struct. Notably, we don't ever use archetypes stored there, so this whole thing goes away. Capacity and grow amount were mostly superficial, as we are already using Vecs inside anyway, so I've got rid of those too. Now the overall table capacity is being driven by the internal entity Vec capacity. This has a side effect of automatically implementing exponential growth pattern for BitVecs reallocations inside Table, which to my measurements slightly improves performance in tests that are heavy on inserts. YMMV, but I hope that those tests were at least remotely correct.
@bors bors bot changed the title reduce tricky unsafety and simplify table structure [Merged by Bors] - reduce tricky unsafety and simplify table structure May 24, 2021
@bors bors bot closed this May 24, 2021
bors bot pushed a commit that referenced this pull request May 30, 2021
Continuing the work on reducing the safety footguns in the code, I've removed one extra `UnsafeCell` in favour of safe `Cell` usage inisde `ComponentTicks`. That change led to discovery of misbehaving component insert logic, where data wasn't properly dropped when overwritten. Apart from that being fixed, some method names were changed to better convey the "initialize new allocation" and "replace existing allocation" semantic.

Depends on #2221, I will rebase this PR after the dependency is merged. For now, review just the last commit.

Co-authored-by: Carter Anderson <[email protected]>
ostwilkens pushed a commit to ostwilkens/bevy that referenced this pull request Jul 27, 2021
I've noticed that we are overusing interior mutability of the Table data, where in many cases we already own a unique reference to it. That prompted a slight refactor aiming to reduce number of safety constraints that must be manually upheld. Now the majority of those are just about avoiding bound checking, which is relatively easy to prove right.

Another aspect is reducing the complexity of Table struct. Notably, we don't ever use archetypes stored there, so this whole thing goes away. Capacity and grow amount were mostly superficial, as we are already using Vecs inside anyway, so I've got rid of those too. Now the overall table capacity is being driven by the internal entity Vec capacity. This has a side effect of automatically implementing exponential growth pattern for BitVecs reallocations inside Table, which to my measurements slightly improves performance in tests that are heavy on inserts. YMMV, but I hope that those tests were at least remotely correct.
ostwilkens pushed a commit to ostwilkens/bevy that referenced this pull request Jul 27, 2021
Continuing the work on reducing the safety footguns in the code, I've removed one extra `UnsafeCell` in favour of safe `Cell` usage inisde `ComponentTicks`. That change led to discovery of misbehaving component insert logic, where data wasn't properly dropped when overwritten. Apart from that being fixed, some method names were changed to better convey the "initialize new allocation" and "replace existing allocation" semantic.

Depends on bevyengine#2221, I will rebase this PR after the dependency is merged. For now, review just the last commit.

Co-authored-by: Carter Anderson <[email protected]>
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-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.

4 participants