FosterFramework / Foster

A small C# game framework
MIT License
450 stars 37 forks source link

Use for loops over foreach in Material.Get/Apply #38

Closed MrBrixican closed 11 months ago

MrBrixican commented 11 months ago

for loops are ~2x faster for small Lists (that we typically deal with here).

In a degenerate case where two alternating textures are drawn via Batcher, the time decreases for these methods (percent time in release mode):

A benchmark via BenchmarkDotNet shows the improvement as well. I also tested Dictionary, Array, and Span (Span with List uses CollectionsMarshal.AsSpan). Here are the full results with 5 uniforms looking for the last uniform in the list.

CaseMean
DictionaryLookup13.26 ns
ArrayForeach16.37 ns
ArraySpanForeach16.53 ns
ArraySpanFor16.55 ns
ArrayFor16.82 ns
ListSpanFor16.93 ns
ListSpanForeach17.28 ns
ListFor18.07 ns
ListForeach31.93 ns

As you can see, we can get slightly better results with List converted to Span using CollectionsMarshal.AsSpan at the cost of some transparency. I personally opted to just change to for loop to maintain simplicity. Dictionary may be tempting but aside from lookups, just about every other operation is slower for low counts. In fact, lookups near the beginning of a List are faster.

NoelFB commented 11 months ago

Huh interesting! I had always assumed foreach and for had similar performance results for simple containers like lists where the enumerator is just a light struct, but it must not inline the function calls or something. Nice change!