Last year I made an article comparing the performance of ToList versus ToArray when creating short lived collections that won’t be mutated, usually used to prevent multiple enumerations when iterating over a temporary LINQ transformation or to ensure mapping exceptions will be thrown inside the corresponding application layer.
The tests were performed with .NET Framework 4.8, .NET 7 and .NET 8, which concluded that ToArray is significantly faster and more memory efficient for almost all collection sizes, with the only exception with very large collections in .NET 8 were ToList was faster - but still uses more memory).
Assuming everything goes as planed, Microsoft should release .NET 9 by the end of 2024. This is the next major version of their most popular development framework that will bring a lot of new features (C# 13 is one of them) and performance improvements.
Since we already have .NET 9 preview 5 available, which contains an even more optimized SegmentedArrayBuilder that is used internally by ToArray, I think it is a good time to compare the performance of both these methods in .NET 9 while using .NET 8 as the baseline.
Performance Test
Once again, I’m going to use the well known C# library BenchmarkDotNet to run the tests and the environment will be the following:
The test consists in the creation of a collection that will hold random integers with a size defined by a test parameter. To ensure the collection initialization doesn’t affect performance, it will be created and cached during test setup, but converted to a new IEnumerable before invoking either ToArray or ToList.
Keep in mind we want to test the performance of iterating over an IEnumerable and create either an array or a list so, to prevent .NET internal optimizations (like using a SelectArrayIterator), the method that converts the cached array to an IEnumerable will use the yield return keyword. This is different than the previous article were I was using the Select method which would return an optimized enumerable for arrays and I want to test the worst case scenario.
Since this article is a continuation of my previous one, were I concluded using ToArray is faster and more memory efficient than ToList, let’s compare if that statement still holds true.
Using ToList as the baseline, we can see the ToArray method is, on average, 15% faster and uses 60% less memory.
Keep in mind that ToArray is a better choice than ToList even on larger collections, something that wasn’t true in .NET 8, were it was 4% slower despite using less memory.
The winner: .NET 9.0
.NET performance evolution
Because we also want to compare the performance of .NET 9 over .NET 8, let’s analyze each method individually on each framework and see if anything has changed.
Using .NET 8 as the baseline, we can see the ToList method is, on average, 15% faster while having exactly the same memory footprint on .NET 9.
The winner: .NET 9.0
Conclusion
In this article we compared the performance of ToArray versus ToList on .NET 9 and concluded, once again, if you need to create a temporary collection in memory to prevent multiple enumerations of an IEnumerable, using ToArray is more performant in all scenarios independent of collection size, something that wasn’t true in .NET 8.
This is also a clear statement that Microsoft made a good decision to introduce classes and structures dedicated to performance, like ArrayPool or ReadOnlySpan, making it easier to share or reuse resources without constantly (de)allocating memory. This has been specially important for our performance tests since SegmentedArrayBuilder makes heavy use of these functionalities, now more than ever.