-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Mitigate stack overflow on large Bundle inserts #20772
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
This reverts commit eb9ea33.
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 quickly skimmed through the second half of the PR so something might be missing there. If this PR alone fixes the stack overflow I feel like we shouldn't deal with unaligned pointers then (see all the comments with the issues they create)
I haven't run benchmarks since #20772 (comment), but I'm fairly confident for small bundles it's still in the noise. For very small bundles (smaller than a pointer), this may be a unnoticeable regression since we're technically moving more on the stack in those cases, but the cost there should be dwarfed by all the metadata shuffling that insertion needs to wrangle. One thing I should do is run a |
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Mike <mike.hsu@gmail.com>
I went ahead and tested this: james7132/bevy_asm_tests@main...stack-overflow-fix. On release, opt-level 3, and fat LTO, there's very little difference. A few extra |
/// function. Calling [`bevy_ptr::deconstruct_moving_ptr`] in this function automatically ensures this. | ||
/// | ||
/// [`Component`]: crate::component::Component | ||
unsafe fn get_components( |
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 it is worth leaving a note here about why we're using MovingPtr
here, with a link to the relevant issue.
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.
Added a few non-doc comments to the trait functions where it's relevant.
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.
Taking on some complexity to mitigate stack overflows feels worth it. Reasonably straightforward in its current form. I don't love the new unnecessary archetype move / redundant bookkeeping (see my reply in the thread above) ... I think it points to missing tools in our toolkit. I'm willing to make that trade in the short term in the interest of moving forward, but I've opened #20976 to track this, and added it to the 0.18 milestone.
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 read the code, but not the full comment thread, so I apologize in advance if I wound up repeating something that was already discussed and explained!
crates/bevy_ecs/src/bundle/info.rs
Outdated
/// which removes the need to look up the [`ArchetypeAfterBundleInsert`](crate::archetype::ArchetypeAfterBundleInsert) | ||
/// in the archetype graph, which requires ownership of the entity's current archetype. | ||
/// | ||
/// Regardless of how this is used, [`apply_effect`] should be called exactly once on `bundle` after this function is |
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.
It's odd for a safety requirement to say "should". Should this say "must", or should it be a non-safety doc comment?
Is failing to call apply_effect
meant to be unsound, or just a safe leak like mem::forget
?
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.
You're right. I changed it to say "must" instead of should, but it's a "at most once before returning to user-space safe code".
#[doc(hidden)] | ||
fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) -> Self::Effect; | ||
} | ||
type Effect; |
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.
Does it still make sense to have Effect
as an associated type here? It doesn't seem like it actually gets used, apart from the NoBundleEffect
trait bound, which could be moved to Self
. We should certainly leave it there for now to make this change smaller, but we may want to remove it as a follow up.
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.
Yep, this is borderline vestigial at this point, but those generic constraints are required for user facing APIs for now. @janis-bhm comment in the same PR notes we should probably keep something like it and return it from get_components
instead of using partial_move
where we're doing it now, but I ran into a few lifetime tangles along the way trying to implement that. I'll file a separate issue to address this at a later point.
where | ||
I: Iterator, | ||
I::Item: Bundle, | ||
I::Item: Bundle<Effect: NoBundleEffect>, |
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.
These constraints are a bug fix, because previously the code allowed bundles with effects but ignored the effects, right?
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.
From a user-facing perspective, no, since this type isn't public and we only used it in spaces where that invariant held anyway, but from the perspective easily provable safety invariants, yes.
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Objective
Fix #20571.
Solution
MovingPtr<'_, T>
of the Bundle instead.MovingPtr<'_, Self>
toDynamicBundle::get_components
and its recursive children.BundleEffect
, directly apply the effect from aMovingPtr<'_, MaybeUninit<Self>>
.BundleEffect
trait.This should avoid most if not all extra stack copies of the bundle and its components. This won't 100% fix stack overflows via bundles, but it does mitigate them until much larger bundles are used.
This started as a subset of the changes made in #20593.
Testing
Ran
cargo r --example feathers --features="experimental_bevy_feathers"
on Windows, no stack overflow.Co-Authored By: janis janis@nirgendwo.xyz