Reflection GetCustomAttributes performance improvement in .NET 8 compared to .NET 7
Date Added (UTC):
15 Apr 2024 @ 18:30
Date Updated (UTC):15 Apr 2024 @ 18:30
.NET Version(s): Tag(s):
#.Net8PerfImprovement #Reflection
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 ReflectionDotnet8
{
[Benchmark]
public object[] GetCustomAttributes() => typeof(C).GetCustomAttributes(typeof(MyAttribute), inherit: true);
[My(Value1 = 1, Value2 = 2)]
class C { }
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
public int Value1 { get; set; }
public int Value2 { get; set; }
}
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.Trend);
}
}
}
Powered by SharpLab
// .NET 7, .NET 8
public object[] GetCustomAttributes()
{
return typeof(C).GetCustomAttributes(typeof(MyAttribute), true);
}
Powered by SharpLab
// .NET 7
.method public hidebysig
instance object[] GetCustomAttributes () 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 0x209d
// Code size 27 (0x1b)
.maxstack 8
// sequence point: (line 26, col 46) to (line 26, col 111) in _
IL_0000: ldtoken ReflectionDotnet8/C
IL_0005: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_000a: ldtoken ReflectionDotnet8/MyAttribute
IL_000f: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_0014: ldc.i4.1
IL_0015: callvirt instance object[] [System.Runtime]System.Reflection.MemberInfo::GetCustomAttributes(class [System.Runtime]System.Type, bool)
IL_001a: ret
}
// .NET 8
.method public hidebysig
instance object[] GetCustomAttributes () 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 27 (0x1b)
.maxstack 8
// sequence point: (line 26, col 46) to (line 26, col 111) in _
IL_0000: ldtoken ReflectionDotnet8/C
IL_0005: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_000a: ldtoken ReflectionDotnet8/MyAttribute
IL_000f: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_0014: ldc.i4.1
IL_0015: callvirt instance object[] [System.Runtime]System.Reflection.MemberInfo::GetCustomAttributes(class [System.Runtime]System.Type, bool)
IL_001a: ret
}
Powered by SharpLab
|
|
Benchmark Description:
A small but welcome improvement.
[PR](https://github.com/dotnet/runtime/pull/87902)
The provided benchmark code is designed to evaluate the performance of reflection operations in .NET, specifically focusing on the `GetCustomAttributes` method. This benchmark is set up using BenchmarkDotNet, a powerful .NET library that makes it easy to create benchmarks that are accurate and reliable. The configuration and setup details, along with the rationale behind the benchmark method, are explained below.
### General Setup and Configuration
- **.NET Versions:** The benchmark is configured to run against two versions of .NET: .NET 7 and .NET 8. This is specified in the `Config` class where two jobs are added, one for each .NET version. This allows for performance comparison across these versions.
- **BenchmarkDotNet Attributes:**
- `HideColumns`: This attribute hides specific columns in the benchmark output (Error, StdDev, Median, RatioSD) to focus on the most relevant data.
- `MemoryDiagnoser`: Enabled with `displayGenColumns` set to false, indicating that memory allocation data will be included in the benchmark results, but without showing the garbage collection generation columns.
- `Config`: Specifies a custom configuration for the benchmark, defined in the `Config` class.
### Benchmark Method: `GetCustomAttributes`
- **Purpose:** This method is designed to measure the performance of retrieving custom attributes applied to a class using reflection. Reflection is a powerful feature in .NET that allows runtime type inspection and invocation. However, it's known to be relatively slow compared to direct code execution, making its performance critical to understand and optimize in performance-sensitive applications.
- **Performance Aspect:** The benchmark specifically tests how quickly the `GetCustomAttributes` method can retrieve attributes of a given type (`MyAttribute`) applied to a class (`C`). This operation is common in scenarios where metadata about classes or members is needed at runtime, such as in custom serialization, ORM mapping, or implementing dependency injection containers.
- **Expected Results/Insights:**
- **Performance Comparison:** By running this benchmark against .NET 7 and .NET 8, you can observe any performance improvements or regressions in the reflection API between these versions.
- **Memory Allocation:** The `MemoryDiagnoser` attribute will provide insights into how much memory is allocated by the `GetCustomAttributes` method. This is important because excessive memory allocation can lead to GC pressure and degraded performance.
- **Attribute Retrieval Efficiency:** The results will show how efficiently attributes can be retrieved at runtime, which can inform decisions about using custom attributes in performance-critical paths of an application.
### Conclusion
This benchmark is a focused test designed to evaluate the performance of a specific reflection operation (`GetCustomAttributes`) in .NET. By comparing the performance across different .NET versions, developers can gain insights into the efficiency of reflection operations and their impact on application performance. The results can guide optimization efforts, especially in scenarios where reflection is used extensively. Understanding the balance between the flexibility provided by reflection and its performance implications is crucial for developing high-performance .NET applications.