Skip to content

Smart Local<Vec<T>> system parameter #8296

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

Open
nicopap opened this issue Apr 3, 2023 · 3 comments
Open

Smart Local<Vec<T>> system parameter #8296

nicopap opened this issue Apr 3, 2023 · 3 comments
Labels
A-ECS Entities, components, systems, and events A-Utils Utility functions and types C-Feature A new feature, making something new possible C-Performance A change motivated by improving speed, memory usage or compile times C-Usability A targeted quality-of-life change that makes Bevy easier to use

Comments

@nicopap
Copy link
Contributor

nicopap commented Apr 3, 2023

What problem does this solve or what need does it fill?

A common design pattern in bevy is the "allocate once Vec".

Some systems require a Vec to accumulate values, we want to avoid allocating or re-allocating the Vec when pushing to it. Especially, we want to avoid allocating the Vec every frame. So we store it in a Local, clear() it at the start of the system and use it, without worrying for allocation.

fn my_system(
    // ...
    mut store: Local<Vec<Foo>>,
) {
    store.clear();
    for foo in &query {
         store.push(foo.bar());
    }
    // do something with store
}

This is in fact a pattern found at least twice in the bevy codebase.

A major limitation of this pattern is burst events where store contains many Foos one frame every minutes or so. It will clog memory for the entirety of the runtime of the application, while staying empty most of the time.

Since it's a common pattern, we can legitimately worry that if this pattern is repeated enough time, we will see too much memory being sat on while not being used.

What solution would you like?

Creating a system parameter encapsulating this pattern makes it discoverable, so that people can learn and grab the low hanging fruits of optimization. It is also a good place to warn users of the potential pitfalls of such a pattern.

I see a few options:

  • Have an actual "Preallocated" system param with a convenient API specifically made for this use case, without memory management, but advising users to be careful about memory management
  • Have this system param type, and a "smart" inter-frame allocator with inter-frame awareness. This feels like the kind of problem domain already largely explored and where it should be possible to find good heuristic.
  • provide a Allocator system param, is that even possible? lol
  • Add a "design pattern" section to the theoretical future bevy book
@nicopap nicopap added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use A-Utils Utility functions and types labels Apr 3, 2023
@james7132
Copy link
Member

Related: #4781 and #4832. We definitely need a way to reign in long-lived, and seldom used chunks of memory. Though what form that takes is a pretty open question.

@JMS55
Copy link
Contributor

JMS55 commented Apr 3, 2023

I can think of an easy solution off the top of my head. How does this sound? Have a type that acts like Local<Vec>, but, at the start of each frame:

  1. It's automatically cleared.
  2. It keeps track of a running average of vec length each frame, and when average_length > capacity * 1.5 (we'll have to figure out a good heuristic), then shrink the vec capacity.

@cactusdualcore
Copy link
Contributor

cactusdualcore commented Apr 12, 2023

It keeps track of a running average of vec length each frame, and when average_length > capacity * 1.5 (we'll have to figure out a good heuristic), then shrink the vec capacity.

Might be just me, but doesn't this sort of still do (though less) frequent re- and deallocation of the Vecs?

average_length > capacity * 1.5

I assume you mean capacity > average_length * 1.5, because the capacity must grow to meet the maximum length (which is bigger or equal to the average) and so cannot be less

@Nilirad Nilirad added the C-Performance A change motivated by improving speed, memory usage or compile times label Jan 19, 2025
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 A-Utils Utility functions and types C-Feature A new feature, making something new possible C-Performance A change motivated by improving speed, memory usage or compile times C-Usability A targeted quality-of-life change that makes Bevy easier to use
Projects
None yet
Development

No branches or pull requests

5 participants