Advertisement
Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
Apr 4

.NET — ToList vs ToArray

.NET — ToList vs ToArray
Автор:
Источник:
Просмотров:
850

Performance comparison between ToList and ToArray

Performance comparison between ToList and ToArray

Ever since Microsoft introduced Language Integrated Query to the .NET framework (also known as LINQ) developers have been using it extensively to work with collections.

From a simple filter, to an aggregation, to a transformation, LINQ is the technology of choice to keep code clean and readable. We even have providers that convert LINQ instructions into SQL commands that will be run in some database.

One of the coding guidelines for the applications I manage dictates the following:

Always use IReadOnlyCollection instead of IEnumerable when passing collections between application layers and ToArray should be used to force the enumeration.

Because we developers are curious by default and need to understand why things are implemented in a given way, every time we have a new team member, that coding guideline usually leads to the following conversation:

Q: Why do we use IReadOnlyCollection in POCOs instead of IEnumerable?

A: Well, because we want the contracts to clearly state the collection is in memory, hence no multiple enumerations will occur, and any mapping problems will happen in the corresponding layer.

Q: Fair enough, but why ToArray? That interface is implemented by arrays and lists, I could be using ToList and have the same result.

A: The result is the same, that’s a fact, but ToArray is usually faster and more memory efficient than ToList, and since it’s a short lived collection that won’t be mutated, the former is preferred.

In this article I’m going to compare the performance of ToList versus ToArray when creating short lived collections. I’m also going to execute the test in different versions of the framework (.NET Framework 4.8, .NET 7 and .NET 8) so we can also see how much the performance have improved over the years.

I’m going to use the well known C# library BenchmarkDotNet to run the tests and the environment will be the following:

BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.2428/22H2/2022Update/SunValley2)
AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores
.NET SDK 8.0.100-rc.2.23502.2
  [Host]             : .NET 8.0.0 (8.0.23.47906), X64 RyuJIT AVX2
  .NET 5.0           : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2
  .NET 7.0           : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2
  .NET 8.0           : .NET 8.0.0 (8.0.23.47906), X64 RyuJIT AVX2
  .NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256

Performance test

The test consists in the creation of a collection that holds random integers, being the size defined by a parameter. To ensure the randomness does not affect the results, the values are cached into an array and before invoking either ToArray or ToList it is converted into a new IEnumerable, not just a cast that could lead to internal optimizations.

[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net70)]
[SimpleJob(RuntimeMoniker.Net80)]
[MemoryDiagnoser]
public class ToListVsToArray
{
    [Params(10, 100, 1000, 10000, 100000)]
    public int Size;

    private int[] _items;

    [GlobalSetup]
    public void Setup()
    {
        var random = new Random(123);

        _items = Enumerable.Range(0, Size).Select(_ => random.Next()).ToArray();
    }

    [Benchmark]
    public int[] ToArray() => CreateItemsEnumerable().ToArray();

    [Benchmark]
    public List<int> ToList() => CreateItemsEnumerable().ToList();

    private IEnumerable<int> CreateItemsEnumerable() => _items.Select(e => e);
}

Performance results

Because we want to decide, for a given application, between ToArray or ToList based on performance, let’s first analyze the results for each framework version.

.NET Framework 4.8

| Method  | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |
|-------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|
| ToArray | 10     |     166.31 ns |     1.013 ns |     0.947 ns |   0.0484 |        - |        - |     305 B |
| ToList  | 10     |     193.17 ns |     1.248 ns |     1.168 ns |   0.0446 |        - |        - |     281 B |
| ToArray | 100    |     965.26 ns |     8.436 ns |     7.479 ns |   0.2594 |        - |        - |    1637 B |
| ToList  | 100    |   1,032.99 ns |     4.144 ns |     3.876 ns |   0.1984 |        - |        - |    1252 B |
| ToArray | 1000   |   8,377.61 ns |    39.382 ns |    36.838 ns |   1.9836 |        - |        - |   12509 B |
| ToList  | 1000   |   8,665.62 ns |    57.777 ns |    54.045 ns |   1.3428 |   0.0153 |        - |    8514 B |
| ToArray | 10000  |  81,576.33 ns |   923.478 ns |   863.822 ns |  26.9775 |   5.3711 |        - |  171755 B |
| ToList  | 10000  |  83,476.64 ns |   410.694 ns |   342.948 ns |  20.7520 |   4.0283 |        - |  131606 B |
| ToArray | 100000 | 830,624.20 ns | 4,536.991 ns | 4,021.924 ns | 399.4141 | 399.4141 | 399.4141 | 1452144 B |
| ToList  | 100000 | 945,017.84 ns | 7,921.731 ns | 6,615.004 ns | 285.1563 | 285.1563 | 285.1563 | 1051184 B |

The ToArray method is, on average, 10% faster than ToList in .NET Framework 4.8.

.NET 7

| Method  | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |
|-------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|
| ToArray | 10     |      50.06 ns |     0.916 ns |     1.254 ns |   0.0134 |        - |        - |     112 B |
| ToList  | 10     |      56.20 ns |     1.022 ns |     0.906 ns |   0.0172 |        - |        - |     144 B |
| ToArray | 100    |     231.18 ns |     1.597 ns |     1.494 ns |   0.0563 |        - |        - |     472 B |
| ToList  | 100    |     261.38 ns |     2.523 ns |     2.236 ns |   0.0601 |        - |        - |     504 B |
| ToArray | 1000   |   2,029.47 ns |    28.534 ns |    25.295 ns |   0.4845 |        - |        - |    4072 B |
| ToList  | 1000   |   2,291.63 ns |    13.328 ns |    11.815 ns |   0.4883 |        - |        - |    4104 B |
| ToArray | 10000  |  17,322.99 ns |   176.548 ns |   165.143 ns |   4.7607 |        - |        - |   40072 B |
| ToList  | 10000  |  22,781.69 ns |   200.720 ns |   177.933 ns |   4.7607 |        - |        - |   40104 B |
| ToArray | 100000 | 306,976.29 ns | 2,525.016 ns | 2,361.901 ns | 124.5117 | 124.5117 | 124.5117 |  400114 B |
| ToList  | 100000 | 337,437.79 ns | 2,441.397 ns | 2,283.684 ns | 124.5117 | 124.5117 | 124.5117 |  400146 B |

The ToArray method is, on average, 13% faster than ToList in .NET 7.

.NET 8

| Method  | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |
|-------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|
| ToArray | 10     |      33.89 ns |     0.727 ns |     0.778 ns |   0.0134 |        - |        - |     112 B |
| ToList  | 10     |      40.17 ns |     0.668 ns |     0.625 ns |   0.0172 |        - |        - |     144 B |
| ToArray | 100    |      90.30 ns |     1.104 ns |     0.922 ns |   0.0564 |        - |        - |     472 B |
| ToList  | 100    |      97.87 ns |     1.257 ns |     1.176 ns |   0.0602 |        - |        - |     504 B |
| ToArray | 1000   |     615.97 ns |     6.819 ns |     5.695 ns |   0.4864 |        - |        - |    4072 B |
| ToList  | 1000   |     615.44 ns |     5.195 ns |     4.056 ns |   0.4902 |        - |        - |    4104 B |
| ToArray | 10000  |   5,335.01 ns |    86.179 ns |    76.395 ns |   4.7607 |        - |        - |   40072 B |
| ToList  | 10000  |   5,427.51 ns |    86.063 ns |    80.503 ns |   4.7836 |        - |        - |   40104 B |
| ToArray | 100000 | 169,711.96 ns | 2,405.080 ns | 2,249.713 ns | 124.7559 | 124.7559 | 124.7559 |  400114 B |
| ToList  | 100000 | 162,577.72 ns | 1,634.437 ns | 1,364.829 ns | 124.7559 | 124.7559 | 124.7559 |  400146 B |

The ToArray method is, by a small margin, faster than ToList in .NET 8, being 4% slower on larger collections.

.NET performance evolution

Because we have results for each framework version, let’s compare and see how .NET performance has evolved over the years.

ToArray

| Runtime            | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |
|------------------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|
| .NET Framework 4.8 | 10     |     166.31 ns |     1.013 ns |     0.947 ns |   0.0484 |        - |        - |     305 B |
| .NET 7.0           | 10     |      50.06 ns |     0.916 ns |     1.254 ns |   0.0134 |        - |        - |     112 B |
| .NET 8.0           | 10     |      33.89 ns |     0.727 ns |     0.778 ns |   0.0134 |        - |        - |     112 B |
| .NET Framework 4.8 | 100    |     965.26 ns |     8.436 ns |     7.479 ns |   0.2594 |        - |        - |    1637 B |
| .NET 7.0           | 100    |     231.18 ns |     1.597 ns |     1.494 ns |   0.0563 |        - |        - |     472 B |
| .NET 8.0           | 100    |      90.30 ns |     1.104 ns |     0.922 ns |   0.0564 |        - |        - |     472 B |
| .NET Framework 4.8 | 1000   |   8,377.61 ns |    39.382 ns |    36.838 ns |   1.9836 |        - |        - |   12509 B |
| .NET 7.0           | 1000   |   2,029.47 ns |    28.534 ns |    25.295 ns |   0.4845 |        - |        - |    4072 B |
| .NET 8.0           | 1000   |     615.97 ns |     6.819 ns |     5.695 ns |   0.4864 |        - |        - |    4072 B |
| .NET Framework 4.8 | 10000  |  81,576.33 ns |   923.478 ns |   863.822 ns |  26.9775 |   5.3711 |        - |  171755 B |
| .NET 7.0           | 10000  |  17,322.99 ns |   176.548 ns |   165.143 ns |   4.7607 |        - |        - |   40072 B |
| .NET 8.0           | 10000  |   5,335.01 ns |    86.179 ns |    76.395 ns |   4.7607 |        - |        - |   40072 B |
| .NET Framework 4.8 | 100000 | 830,624.20 ns | 4,536.991 ns | 4,021.924 ns | 399.4141 | 399.4141 | 399.4141 | 1452144 B |
| .NET 7.0           | 100000 | 306,976.29 ns | 2,525.016 ns | 2,361.901 ns | 124.5117 | 124.5117 | 124.5117 |  400114 B |
| .NET 8.0           | 100000 | 169,711.96 ns | 2,405.080 ns | 2,249.713 ns | 124.7559 | 124.7559 | 124.7559 |  400114 B |

In some cases being 90% faster than .NET Framework 4.8 and more than 50% faster than .NET 7 while allocating less memory, .NET 8 is a clear winner.

ToList

| Runtime            | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |
|------------------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|
| .NET Framework 4.8 | 10     |     193.17 ns |     1.248 ns |     1.168 ns |   0.0446 |        - |        - |     281 B |
| .NET 7.0           | 10     |      56.20 ns |     1.022 ns |     0.906 ns |   0.0172 |        - |        - |     144 B |
| .NET 8.0           | 10     |      40.17 ns |     0.668 ns |     0.625 ns |   0.0172 |        - |        - |     144 B |
| .NET Framework 4.8 | 100    |   1,032.99 ns |     4.144 ns |     3.876 ns |   0.1984 |        - |        - |    1252 B |
| .NET 7.0           | 100    |     261.38 ns |     2.523 ns |     2.236 ns |   0.0601 |        - |        - |     504 B |
| .NET 8.0           | 100    |      97.87 ns |     1.257 ns |     1.176 ns |   0.0602 |        - |        - |     504 B |
| .NET Framework 4.8 | 1000   |   8,665.62 ns |    57.777 ns |    54.045 ns |   1.3428 |   0.0153 |        - |    8514 B |
| .NET 7.0           | 1000   |   2,291.63 ns |    13.328 ns |    11.815 ns |   0.4883 |        - |        - |    4104 B |
| .NET 8.0           | 1000   |     615.44 ns |     5.195 ns |     4.056 ns |   0.4902 |        - |        - |    4104 B |
| .NET Framework 4.8 | 10000  |  83,476.64 ns |   410.694 ns |   342.948 ns |  20.7520 |   4.0283 |        - |  131606 B |
| .NET 7.0           | 10000  |  22,781.69 ns |   200.720 ns |   177.933 ns |   4.7607 |        - |        - |   40104 B |
| .NET 8.0           | 10000  |   5,427.51 ns |    86.063 ns |    80.503 ns |   4.7836 |        - |        - |   40104 B |
| .NET Framework 4.8 | 100000 | 945,017.84 ns | 7,921.731 ns | 6,615.004 ns | 285.1563 | 285.1563 | 285.1563 | 1051184 B |
| .NET 7.0           | 100000 | 337,437.79 ns | 2,441.397 ns | 2,283.684 ns | 124.5117 | 124.5117 | 124.5117 |  400146 B |
| .NET 8.0           | 100000 | 162,577.72 ns | 1,634.437 ns | 1,364.829 ns | 124.7559 | 124.7559 | 124.7559 |  400146 B |

Once again, .NET 8 takes the gold!

Conclusion

In this article we compared the performance of ToArray vs ToList and concluded the former performs better most of the time by being faster and more memory efficient, so consider using it when creating short lived collections were enumeration must be forced.

We also concluded that .NET 8, the version still to be released, will bring fantastic performance upgrades in this regards. Not only it is significantly faster than the older versions, it also brings ToArray and ToList so close that it’s almost indifferent which method should be used.

Похожее
Jan 1, 2023
Author: Walter Guevara
By software engineering standards, ASP.NET Web Forms can be considered old school. Perhaps I’m aging myself with that statement, but its true. From a software perspective, even 10 years begins to show its age. I started to use Web Forms...
Oct 24, 2023
Author: Itexamtools.com
Want to switch to that high paying job? Or are you already been preparing hard to give interview the next weekend?Do you know how many people get rejected in interviews by preparing only concepts but not focusing on actually which...
Dec 21, 2023
Author: Jeremy Wells
Introduction and prerequisitesThis post is part of an ongoing series where we build a “walking skeleton” application using ASP.NET Core and Angular as well as other technologies for deployment and testing. By now, our application is a minimally functional web...
Jan 1, 2023
Author: Matt Eland
New and old ways of creating your own exceptions in dotnet with C# 11 and .NET 7.Let’s talk about building custom exceptions in C# code and why you’d want to do that. We’ll cover the traditional way as well as...
Написать сообщение
Почта
Имя
*Сообщение


© 1999–2024 WebDynamics
1980–... Sergey Drozdov
Area of interests: .NET Framework | .NET Core | C# | ASP.NET | Windows Forms | WPF | HTML5 | CSS3 | jQuery | AJAX | Angular | React | MS SQL Server | Transact-SQL | ADO.NET | Entity Framework | IIS | OOP | OOA | OOD | WCF | WPF | MSMQ | MVC | MVP | MVVM | Design Patterns | Enterprise Architecture | Scrum | Kanban