1
Enum performance improvements in .NET 8 compared to .NET 7
Date Added (UTC):
15 Apr 2024 @ 19:07
Date Updated (UTC):15 Apr 2024 @ 19:10
.NET Version(s): Tag(s):
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;
[HideColumns("Error", "StdDev", "Median", "RatioSD")]
[MemoryDiagnoser(displayGenColumns: false)]
[Config(typeof(Config))]
public class EnumBenchmarks
{
private readonly DayOfWeek _dow = DayOfWeek.Saturday;
[Benchmark] public bool IsDefined() => Enum.IsDefined(_dow);
[Benchmark] public string GetName() => Enum.GetName(_dow);
[Benchmark] public string[] GetNames() => Enum.GetNames<DayOfWeek>();
[Benchmark] public DayOfWeek[] GetValues() => Enum.GetValues<DayOfWeek>();
[Benchmark] public Array GetUnderlyingValues() => Enum.GetValuesAsUnderlyingType<DayOfWeek>();
[Benchmark] public string EnumToString() => _dow.ToString();
[Benchmark] public bool TryParse() => Enum.TryParse<DayOfWeek>("Saturday", out _);
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 bool IsDefined()
{
return Enum.IsDefined(_dow);
}
// .NET 7, .NET 8
public string GetName()
{
return Enum.GetName(_dow);
}
// .NET 7, .NET 8
public string[] GetNames()
{
return Enum.GetNames<DayOfWeek>();
}
// .NET 7, .NET 8
public DayOfWeek[] GetValues()
{
return Enum.GetValues<DayOfWeek>();
}
// .NET 7, .NET 8
public Array GetUnderlyingValues()
{
return Enum.GetValuesAsUnderlyingType<DayOfWeek>();
}
// .NET 7
public string EnumToString()
{
return _dow.ToString();
}
// .NET 8
public string EnumToString()
{
DayOfWeek dow = _dow;
return dow.ToString();
}
// .NET 7, .NET 8
public bool TryParse()
{
DayOfWeek result;
return Enum.TryParse<DayOfWeek>("Saturday", out result);
}
Powered by SharpLab
// .NET 7
.method public hidebysig
instance bool IsDefined () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1b 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x209d
// Code size 12 (0xc)
.maxstack 8
// sequence point: (line 27, col 44) to (line 27, col 64) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.DayOfWeek EnumBenchmarks::_dow
IL_0006: call bool [System.Runtime]System.Enum::IsDefined<valuetype [System.Runtime]System.DayOfWeek>(!!0)
IL_000b: ret
}
// .NET 8
.method public hidebysig
instance bool IsDefined () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1b 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2050
// Code size 12 (0xc)
.maxstack 8
// sequence point: (line 27, col 44) to (line 27, col 64) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.DayOfWeek EnumBenchmarks::_dow
IL_0006: call bool [System.Runtime]System.Enum::IsDefined<valuetype [System.Runtime]System.DayOfWeek>(!!0)
IL_000b: ret
}
// .NET 7
.method public hidebysig
instance string GetName () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1c 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x20aa
// Code size 12 (0xc)
.maxstack 8
// sequence point: (line 28, col 44) to (line 28, col 62) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.DayOfWeek EnumBenchmarks::_dow
IL_0006: call string [System.Runtime]System.Enum::GetName<valuetype [System.Runtime]System.DayOfWeek>(!!0)
IL_000b: ret
}
// .NET 8
.method public hidebysig
instance string GetName () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1c 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x205d
// Code size 12 (0xc)
.maxstack 8
// sequence point: (line 28, col 44) to (line 28, col 62) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.DayOfWeek EnumBenchmarks::_dow
IL_0006: call string [System.Runtime]System.Enum::GetName<valuetype [System.Runtime]System.DayOfWeek>(!!0)
IL_000b: ret
}
// .NET 7
.method public hidebysig
instance string[] GetNames () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1d 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x20b7
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 29, col 47) to (line 29, col 73) in _
IL_0000: call string[] [System.Runtime]System.Enum::GetNames<valuetype [System.Runtime]System.DayOfWeek>()
IL_0005: ret
}
// .NET 8
.method public hidebysig
instance string[] GetNames () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1d 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x206a
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 29, col 47) to (line 29, col 73) in _
IL_0000: call string[] [System.Runtime]System.Enum::GetNames<valuetype [System.Runtime]System.DayOfWeek>()
IL_0005: ret
}
// .NET 7
.method public hidebysig
instance valuetype [System.Runtime]System.DayOfWeek[] GetValues () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1e 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x20be
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 30, col 51) to (line 30, col 78) in _
IL_0000: call !!0[] [System.Runtime]System.Enum::GetValues<valuetype [System.Runtime]System.DayOfWeek>()
IL_0005: ret
}
// .NET 8
.method public hidebysig
instance valuetype [System.Runtime]System.DayOfWeek[] GetValues () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1e 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2071
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 30, col 51) to (line 30, col 78) in _
IL_0000: call !!0[] [System.Runtime]System.Enum::GetValues<valuetype [System.Runtime]System.DayOfWeek>()
IL_0005: ret
}
// .NET 7
.method public hidebysig
instance class [System.Runtime]System.Array GetUnderlyingValues () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1f 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x20c5
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 31, col 55) to (line 31, col 98) in _
IL_0000: call class [System.Runtime]System.Array [System.Runtime]System.Enum::GetValuesAsUnderlyingType<valuetype [System.Runtime]System.DayOfWeek>()
IL_0005: ret
}
// .NET 8
.method public hidebysig
instance class [System.Runtime]System.Array GetUnderlyingValues () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1f 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2078
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 31, col 55) to (line 31, col 98) in _
IL_0000: call class [System.Runtime]System.Array [System.Runtime]System.Enum::GetValuesAsUnderlyingType<valuetype [System.Runtime]System.DayOfWeek>()
IL_0005: ret
}
// .NET 7
.method public hidebysig
instance string EnumToString () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 20 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x20cc
// Code size 18 (0x12)
.maxstack 8
// sequence point: (line 32, col 49) to (line 32, col 64) in _
IL_0000: ldarg.0
IL_0001: ldflda valuetype [System.Runtime]System.DayOfWeek EnumBenchmarks::_dow
IL_0006: constrained. [System.Runtime]System.DayOfWeek
IL_000c: callvirt instance string [System.Runtime]System.Object::ToString()
IL_0011: ret
}
// .NET 8
.method public hidebysig
instance string EnumToString () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 20 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2080
// Code size 21 (0x15)
.maxstack 1
.locals init (
[0] valuetype [System.Runtime]System.DayOfWeek
)
// sequence point: (line 32, col 49) to (line 32, col 64) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.DayOfWeek EnumBenchmarks::_dow
IL_0006: stloc.0
IL_0007: ldloca.s 0
IL_0009: constrained. [System.Runtime]System.DayOfWeek
IL_000f: callvirt instance string [System.Runtime]System.Object::ToString()
IL_0014: ret
}
// .NET 7
.method public hidebysig
instance bool TryParse () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 21 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x20e0
// Code size 13 (0xd)
.maxstack 2
.locals init (
[0] valuetype [System.Runtime]System.DayOfWeek
)
// sequence point: (line 33, col 43) to (line 33, col 86) in _
IL_0000: ldstr "Saturday"
IL_0005: ldloca.s 0
IL_0007: call bool [System.Runtime]System.Enum::TryParse<valuetype [System.Runtime]System.DayOfWeek>(string, !!0&)
IL_000c: ret
}
// .NET 8
.method public hidebysig
instance bool TryParse () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 21 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x20a4
// Code size 13 (0xd)
.maxstack 2
.locals init (
[0] valuetype [System.Runtime]System.DayOfWeek
)
// sequence point: (line 33, col 43) to (line 33, col 86) in _
IL_0000: ldstr "Saturday"
IL_0005: ldloca.s 0
IL_0007: call bool [System.Runtime]System.Enum::TryParse<valuetype [System.Runtime]System.DayOfWeek>(string, !!0&)
IL_000c: ret
}
Powered by SharpLab
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Benchmark Description:
Some pretty big rewrites when into enum for .NET 8, here's the main [PR](https://github.com/dotnet/runtime/pull/78580).
The provided benchmark code is designed to measure the performance of various operations related to enumeration types (`enum`) in .NET, specifically focusing on the `DayOfWeek` enumeration. This benchmark is set up using BenchmarkDotNet, a powerful library for benchmarking .NET code. The configuration and each benchmark method aim to provide insights into different aspects of working with enums, from basic operations like checking if a value is defined within an enum to converting enum values to strings or arrays. Let's delve into the setup and the rationale behind each method.
### General Setup Overview
- **.NET Versions:** The benchmarks are configured to run against two different versions of .NET, specifically .NET 7 and .NET 8. This allows for comparison across versions to see if there are performance improvements or regressions.
- **BenchmarkDotNet Configuration:** The `Config` class customizes the benchmarking environment. It hides certain columns (Error, StdDev, Median, RatioSD) for clarity, disables the display of garbage collection generation columns, and sets up jobs for .NET 7 and .NET 8, with .NET 7 as the baseline. This means results from .NET 8 will be compared against .NET 7 to gauge performance changes.
- **MemoryDiagnoser:** Enabled with `displayGenColumns: false` to track memory allocations without showing garbage collection details.
### Benchmark Methods
1. **IsDefined()**
- **Purpose:** Measures the performance of checking if a specific value is defined within the `DayOfWeek` enum.
- **Importance:** This operation is crucial when validating if a given enum value exists, ensuring that methods or functions that rely on enum values are passed valid data.
- **Expected Insights:** The speed and efficiency of the `IsDefined` method across different .NET versions, highlighting potential performance optimizations in newer frameworks.
2. **GetName()**
- **Purpose:** Benchmarks the time taken to retrieve the name (as a string) of the enum value.
- **Importance:** Converting enum values to their string names is common in logging, UI representation, or serialization tasks.
- **Expected Insights:** How quickly enum names can be retrieved, which can be critical for operations requiring human-readable formats or serialization.
3. **GetNames()**
- **Purpose:** Measures the performance of getting all names defined in the `DayOfWeek` enum as an array of strings.
- **Importance:** Useful for generating lists or dropdowns in UIs, or for validation purposes.
- **Expected Insights:** The efficiency of fetching all enum names, which might impact startup performance or dynamic UI generation.
4. **GetValues()**
- **Purpose:** Benchmarks the time to retrieve all values of the `DayOfWeek` enum as an array.
- **Importance:** Similar to `GetNames`, but for scenarios where the numeric values are needed instead of strings.
- **Expected Insights:** The performance of enumerating all values, affecting similar use cases as `GetNames`.
5. **GetUnderlyingValues()**
- **Purpose:** Measures the performance of retrieving the underlying values of the enum members.
- **Importance:** Useful for low-level operations or optimizations where working directly with the underlying numeric types is necessary.
- **Expected Insights:** Insights into the overhead of abstracting enum values to their underlying numeric representations.
6. **EnumToString()**
- **Purpose:** Benchmarks converting an enum value to its string representation using the `ToString` method.
- **Importance:** A very common operation, especially in logging or displaying information to the user.
- **Expected Insights:** The efficiency of the `ToString` method, which can impact logging or UI responsiveness.
7. **TryParse()**
- **Purpose:** Measures the performance of parsing a string to an enum value, including validation of the string value.
- **Importance:** Critical for deserializing data or accepting user input that needs to be converted to enum types.
- **Expected Insights:** The speed and efficiency of parsing strings to enum values, highlighting potential bottlenecks in input handling or data processing.
### Conclusion
Running these benchmarks will provide a comprehensive view of the performance characteristics of enum operations in .NET, across different versions. Understanding these aspects can help developers write more efficient code, especially in scenarios where enums are heavily used. The insights gained can guide optimizations, influence design decisions, and highlight areas where newer .NET versions offer improvements.