Performance of value-type vs reference-type enumerators

Warp Speed by aalmada

Enumerable.Empty<T>()

In a previous article I focused on Enumerable.Empty() as it’s the simplest possible implementation of IEnumerable. I used my own version of the code that is easier to understand, compared to the one currently found in LINQ.

Benchmarks

I created a benchmark using BenchmarkDotNet containing all these implementations and here we have the results:

  1. The EmptyDisposableStruct<T>, that is a value-type, is 33x times faster than the EmptyClass (the new LINQ version) but 2x slower when casted to IEnumerable<T>.
  2. Two instances of EmptyDisposableStruct<T> are boxed into the heap (2 x 24B) when casted to IEnumerable<T>.
  3. EmptyClass (the new LINQ version) uses references to instances previously allocated on the heap so there are no heap allocations during the benchmark method.
  4. LinqEmpty (the current LINQ implementation) is based on EmptyBoxedArray so they have the same performance.
  5. EmptyClass (the new LINQ version) is 30% faster than LinqEmpty (the current LINQ implementation).
  6. The slowest is EmptyBoxedList

Conclusion

Although EmptyDisposableStruct is much faster than EmptyClass (the new LINQ version), the result of Enumerable.Empty<T>() is usually returned by methods that output IEnumerable<T>. This results in all value-typed implementation to end up ‘boxed’. There’s no much use for explicitly foreach an Enumerable.Empty<T>(). The use of a reference-typed singleton seems to be a great call by the .NET team.

Principal Engineer @ Farfetch - Future Retail Lab https://about.me/antao.almada

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store