Enumerable.Range(0, X).ToList() performance improvements in .NET 8
Date Added (UTC):
08 Apr 2024 @ 02:06
Date Updated (UTC):08 Apr 2024 @ 02:06
.NET Version(s): Tag(s):
#.Net8PerfImprovement #Collections #LINQ
Added By:
.NET Developer and tech lead from Ireland!
Benchmark Results:
Benchmark Code:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using System.Collections.Generic;
using System.Linq;
[Config(typeof(Config))]
[HideColumns("Error", "StdDev", "Median", "RatioSD", "EnvironmentVariables", "Runtime")]
public class RangeToListBenchmark
{
[Benchmark]
public List<int> RangeToList() => Enumerable.Range(0, 16_384).ToList();
private class Config : ManualConfig
{
public Config()
{
AddJob(Job.Default.WithId(".NET 7").WithRuntime(CoreRuntime.Core70).AsBaseline());
AddJob(Job.Default.WithId(".NET 8").WithRuntime(CoreRuntime.Core80));
SummaryStyle =
SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage);
}
}
}
Powered by SharpLab
// .NET 7, .NET 8
public List<int> RangeToList()
{
return Enumerable.ToList(Enumerable.Range(0, 16384));
}
Powered by SharpLab
// .NET 7
.method public hidebysig
instance class [System.Collections]System.Collections.Generic.List`1<int32> RangeToList () cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
01 00 01 00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 19 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x208e
// Code size 17 (0x11)
.maxstack 8
// sequence point: (line 26, col 39) to (line 26, col 75) in _
IL_0000: ldc.i4.0
IL_0001: ldc.i4 16384
IL_0006: call class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> [System.Linq]System.Linq.Enumerable::Range(int32, int32)
IL_000b: call class [System.Collections]System.Collections.Generic.List`1<!!0> [System.Linq]System.Linq.Enumerable::ToList<int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0010: ret
}
// .NET 8
.method public hidebysig
instance class [System.Collections]System.Collections.Generic.List`1<int32> RangeToList () cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
01 00 01 00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 19 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2050
// Code size 17 (0x11)
.maxstack 8
// sequence point: (line 26, col 39) to (line 26, col 75) in _
IL_0000: ldc.i4.0
IL_0001: ldc.i4 16384
IL_0006: call class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> [System.Linq]System.Linq.Enumerable::Range(int32, int32)
IL_000b: call class [System.Collections]System.Collections.Generic.List`1<!!0> [System.Linq]System.Linq.Enumerable::ToList<int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0010: ret
}
Powered by SharpLab
|
|
Benchmark Description:
[PR](https://github.com/dotnet/runtime/pull/87992)
The provided benchmark code is designed to measure the performance of converting a range of integers to a list using the `Enumerable.Range` method followed by the `ToList` method in .NET. This benchmark is configured to run on two different versions of the .NET runtime, specifically .NET 7 and .NET 8, allowing for a comparison of performance across these versions. The configuration and setup for this benchmark are specified using attributes and a custom configuration class that extends `BenchmarkDotNet`'s `ManualConfig`.
### General Setup
- **.NET Versions:** The benchmark is explicitly set up to run on .NET 7 and .NET 8, allowing for direct performance comparisons between these two versions. This is achieved by adding two jobs in the configuration, each specifying the runtime version.
- **BenchmarkDotNet:** This is a powerful library for benchmarking .NET code, providing detailed performance metrics. It's used here to define, run, and report the benchmarks.
- **Configuration:** The custom configuration class `Config` extends `ManualConfig` from BenchmarkDotNet, specifying the jobs (runtime versions) and customizing the summary style. The summary style is set to display ratios in percentage form, making it easier to understand the performance differences between .NET versions.
- **Columns:** The benchmark class is annotated to hide certain columns in the output (`Error`, `StdDev`, `Median`, `RatioSD`, `EnvironmentVariables`, `Runtime`), focusing the report on the most relevant metrics for this test.
### Benchmark Method: `RangeToList`
- **Purpose:** This method is designed to measure the performance of generating a sequence of integers using `Enumerable.Range` and then converting this sequence to a `List<int>` using the `ToList` method. This is a common operation in .NET applications where a range of values needs to be materialized into a list for further processing.
- **Performance Aspect:** The benchmark tests the efficiency of the `Range` and `ToList` methods, including how quickly the range can be generated and converted to a list, and the memory allocations involved in this process. This can provide insights into the overhead of such operations in .NET applications.
- **Why It's Important:** Understanding the performance of converting ranges to lists is crucial for optimizing applications that perform this operation frequently, especially when dealing with large ranges. It can impact both the speed and memory usage of an application.
- **Expected Results/Insights:** By running this benchmark, you can expect to gain insights into:
- The performance difference in executing the `RangeToList` operation between .NET 7 and .NET 8, helping to identify if newer versions of the runtime offer optimizations for such operations.
- The overall efficiency of creating lists from ranges in terms of execution time and memory allocation, which can inform decisions about using this pattern in performance-critical parts of an application.
In summary, this benchmark is a targeted test designed to measure and compare the performance of a specific operation (`Enumerable.Range` followed by `ToList`) across different .NET runtime versions. The insights gained can guide optimizations and inform decisions about application architecture and coding patterns.