Skip to content

Implement SystemParam for SmallVec#24405

Open
Shatur wants to merge 4 commits into
bevyengine:mainfrom
simgine:smallvec-system-param
Open

Implement SystemParam for SmallVec#24405
Shatur wants to merge 4 commits into
bevyengine:mainfrom
simgine:smallvec-system-param

Conversation

@Shatur
Copy link
Copy Markdown
Contributor

@Shatur Shatur commented May 23, 2026

Objective

To register a system from a script, I need to support for dynamic number of system parameters (since scripting languages usually don't play well with generics). Right now the only solution is to use Vec<T> as a system param which is allocated every time the system runs. But it would be nice to avoid allocations when the number of system parameters is low.

Solution

Implement SystemParam for SmallVec. It's already a dependency for bevy_ecs and allows to avoid allocations with user-defined number of system parameters.

Testing

  • Did you test these changes? If so, how? I tried creating a system with a SmallVec<[Local<usize>; 8]>.
  • Are there any parts that need more testing? I don't think so. The code follows Vec impl.

@Shatur
Copy link
Copy Markdown
Contributor Author

Shatur commented May 23, 2026

Looks like CI failure is unrelated.

@kfc35 kfc35 added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 23, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in ECS May 23, 2026
Copy link
Copy Markdown
Contributor

@kfc35 kfc35 left a comment

Choose a reason for hiding this comment

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

Looks good to me, follows the regular Vec impl except for one thing: the unsafe impl for ParamSet<'_, '_, SmallVec<[T; N]>>. Is that also necessary to implement? I assume since it works for you, it doesn’t need to be done but just figured I’d ask the question (for my learning)

@kfc35 kfc35 added the D-Straightforward Simple bug fixes and API improvements, docs, test and examples label May 23, 2026
@Shatur
Copy link
Copy Markdown
Contributor Author

Shatur commented May 23, 2026

Is that also necessary to implement?

I personally don't need it, just implemented it for consistency (since Vec has it.)

Copy link
Copy Markdown
Contributor

@chescock chescock left a comment

Choose a reason for hiding this comment

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

Oh, yay, I'm happy to see SystemParamBuilder is getting use!

But it would be nice to avoid allocations when the number of system parameters is low.

Hmm, I wonder whether we could even make a Vec-like parameter that avoids per-run allocations entirely by re-using a buffer between runs.


// SAFETY: Registers access for each element of `state`.
// If any one conflicts, it will panic.
unsafe impl<T: SystemParam, const N: usize> SystemParam for SmallVec<[T; N]> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think you also need an impl<...> SystemParamBuilder<SmallVec<[P; N]>> for SmallVec<[B; N]> in builder.rs so that you can actually configure the systems.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah, right, I didn't test the builder yet 😅 Added!

}
}

impl<T: SystemParam, const N: usize> ParamSet<'_, '_, SmallVec<[T; N]>> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It looks like you copied the impl block for ParamSet<Vec<_>>, but not the impl SystemParam, so there's no way to actually obtain a ParamSet<SmallVec>.

I don't think you need a SmallVec version of ParamSet, though. ParamSet<Vec> never actually allocates a Vec, and just creates the subparameters on-demand in get_mut, so there is no allocation to save. So I'd be inclined to delete this impl block.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, removed!

@Shatur
Copy link
Copy Markdown
Contributor Author

Shatur commented May 24, 2026

Hmm, I wonder whether we could even make a Vec-like parameter that avoids per-run allocations entirely by re-using a buffer between runs.

This was my original goal, but I couldn't find where to store the Vec, so I went with SmallVec. If you have any idea, I'm happy to try.

@chescock
Copy link
Copy Markdown
Contributor

Hmm, I wonder whether we could even make a Vec-like parameter that avoids per-run allocations entirely by re-using a buffer between runs.

This was my original goal, but I couldn't find where to store the Vec, so I went with SmallVec. If you have any idea, I'm happy to try.

I did a few experiments, but without much success.

The "buffer reuse" technique from Wild Performance Tricks seems like it should work, but if you try to put a Vec<T::Item<'static, 'static>> in the state then the compiler complains that it can't prove Self::Item::State == Self::State because it can't prove T::Item<'w, 's>::Item<'static, 'static> == T::Item<'static, 'static>.

Then I thought that a Vec that dropped its contents but didn't deallocate sounded like bumpalo::collections::Vec, which we already have a dependency on! But the Item type would be Vec<'state, T::Item<'world, 'state>> and that's invalid because it requires T::Item<'world, 'state>: 'state, which doesn't hold.

I think I might have gotten it working using raw pointers! You can use alloc::alloc() to manually allocate a buffer and hold the raw pointer in the State. It's a lot of unsafe, though.

@Shatur
Copy link
Copy Markdown
Contributor Author

Shatur commented May 24, 2026

I think I might have gotten it working using raw pointers! You can use alloc::alloc() to manually allocate a buffer and hold the raw pointer in the State. It's a lot of unsafe, though.

Would be interesting to see!

@Shatur Shatur requested a review from alice-i-cecile May 24, 2026 15:32
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-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

3 participants