The documentation of Entities recommends iteration through a Bag<int> of entities with foreach. However, due to Bag<T> not providing a duck-typed enumerator for foreach, this causes memory allocation. If a system processes a large number of entities (let's say 100k, which is reasonable for an ECS framework) this will cause 100k BagEnumerator allocations per frame (!) due to the boxing conversion when the internal BagEnumerator is converted to IEnumerator.
This PR fixes this by simply adding a duck-typed GetEnumerator() method, and making BagEnumerator public. It also adds a unit test to prevent future issues while using foreach.
Thanks for your attention, this is a very interesting article. I have checked this anomaly in benchmark, and saw worst results: the duck-typed version is 5 times more effective.
The documentation of Entities recommends iteration through a
Bag<int>
of entities withforeach
. However, due toBag<T>
not providing a duck-typed enumerator forforeach
, this causes memory allocation. If a system processes a large number of entities (let's say 100k, which is reasonable for an ECS framework) this will cause 100kBagEnumerator
allocations per frame (!) due to the boxing conversion when the internalBagEnumerator
is converted toIEnumerator
.More information about this problem can be found here: https://nede.dev/blog/preventing-unnecessary-allocation-in-net-collections
This PR fixes this by simply adding a duck-typed
GetEnumerator()
method, and makingBagEnumerator
public. It also adds a unit test to prevent future issues while usingforeach
.