C # `yield return` creates a lot of garbage for me. Can this help?

I am developing an Xbox 360 game with XNA. I would really like to use the C # yield return construct in several places, but it seems to create a lot of garbage. Take a look at this code:

 class ComponentPool<T> where T : DrawableGameComponent { List<T> preallocatedComponents; public IEnumerable<T> Components { get { foreach (T component in this.preallocatedComponents) { // Enabled often changes during iteration over Components // for example, it not uncommon for bullet components to get // disabled during collision testing // sorry I didn't make that clear originally if (component.Enabled) { yield return component; } } } } ... 

I use these component pools everywhere - for bullets, enemies, explosions; nothing numerous and transitory. I often have to iterate over the contents, and I am only interested in the active components (i.e. Enabled == true ), therefore the behavior of the Components property.

Currently, I see up to 800K per second of extra garbage when using this technique. Can this be avoided? Is there any other way to use yield return ?

Edit: I found this question about a broader issue of how to iterate over a resource pool without creating garbage. Many commentators were dismissive, apparently not understanding the limitations of the Compact Framework, but this commentator was prettier and suggested creating an iterator pool. This is the solution I'm going to use.

+6
source share
2 answers

Implementation of iterators by the compiler really uses class objects, and using (with foreach, for example) an iterator implemented with yield return will actually cause memory allocation. In the scheme of things, this is rarely a problem, because either significant work is performed when the iteration is performed, or a much larger amount of memory is allocated during the iteration by other things.

For the memory allocated by the iterator to become a problem, your application must be intensive, and your algorithms must work with objects without allocating any memory. Think of the Games of Life of something like this. Suddenly, the iteration itself overflows. And when the iteration allocates memory, a huge amount of memory can be allocated.

If your application matches this profile (and only if), then the first rule you should follow:

  • Avoid iterators in inner loops when a simpler iterative concept is available.

For example, if you have an array or list, such as a data structure, you already expand the indexer property and the count property so that clients can simply use the for loop instead of using foreach with your iterator. This is “easy money” for shortening GC, and it does not make your code ugly or bloated, a little less elegant.

The second principle that you must follow is:

  • measure memory allocations to see when and where you should use the first rule.
+2
source

Just for fun, try taking a filter in a Linq query and holding an instance of the query. This can reduce memory redistribution every time a request is enumerated.

If nothing else, the preallocatedComponents.Where(r => r.Enabled) is a hell of a lot less code to look like a return.

 class ComponentPool<T> where T : DrawableGameComponent { List<T> preallocatedComponents; IEnumerable<T> enabledComponentsFilter; public ComponentPool() { enabledComponentsFilter = this.preallocatedComponents.Where(r => r.Enabled); } public IEnumerable<T> Components { get { return enabledComponentsFilter; } } ... 
+3
source

Source: https://habr.com/ru/post/886987/


All Articles