1


GetGenericTypeDefinition reflection .NET 8 performance improvement




Date Added (UTC):

15 Apr 2024 @ 18:37

Date Updated (UTC):

15 Apr 2024 @ 18:37


.NET Version(s):

.NET 7 .NET 8

Tag(s):

#.Net8PerfImprovement #Reflection


Added By:
Profile Image

Blog   
Ireland    
.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;
using System.Collections.Generic;

[HideColumns("Error", "StdDev", "Median", "RatioSD")]
[MemoryDiagnoser(displayGenColumns: false)]
[Config(typeof(Config))]
public class ReflectionGenericType
{
    private readonly Type _type = typeof(List<int>);

    [Benchmark] public Type GetGenericTypeDefinition() => _type.GetGenericTypeDefinition();

    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);
        }
    }
}

// .NET 7, .NET 8
public Type GetGenericTypeDefinition()
{
    return _type.GetGenericTypeDefinition();
}

// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Type GetGenericTypeDefinition () 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 1c 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x209d
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 28, col 59) to (line 28, col 91) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Type ReflectionGenericType::_type
    IL_0006: callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Type::GetGenericTypeDefinition()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Type GetGenericTypeDefinition () 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 1c 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2050
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 28, col 59) to (line 28, col 91) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Type ReflectionGenericType::_type
    IL_0006: callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Type::GetGenericTypeDefinition()
    IL_000b: ret
}

// .NET 7 (X64)
GetGenericTypeDefinition()
    L0000: mov rcx, [rcx+8]
    L0004: mov rax, [rcx]
    L0007: mov rax, [rax+0x68]
    L000b: jmp qword ptr [rax+8]
// .NET 8 (X64)
GetGenericTypeDefinition()
    L0000: mov rcx, [rcx+8]
    L0004: mov rax, [rcx]
    L0007: mov rax, [rax+0x68]
    L000b: jmp qword ptr [rax+0x18]


Benchmark Description:


PR = https://github.com/dotnet/runtime/pull/78288

The provided benchmark code is designed to evaluate the performance of a specific reflection operation in .NET, specifically the `GetGenericTypeDefinition` method on a `Type` object. This benchmark is set up using the BenchmarkDotNet library, a popular .NET library for benchmarking code performance. Let's break down the setup and the rationale behind the benchmark method. ### General Setup - **.NET Version**: The benchmark configuration specifies two runtime environments, .NET 7 and .NET 8, allowing for performance comparisons between these two versions. This is achieved by defining two jobs in the `Config` class, one for each .NET version, with `.NET 7` being marked as the baseline. This means the performance results for `.NET 8` will be compared against `.NET 7`. - **BenchmarkDotNet Configuration**: The benchmark class is annotated with attributes that control how the benchmark results are reported. `HideColumns` is used to simplify the output by hiding specific columns like error, standard deviation, median, and the ratio standard deviation. `MemoryDiagnoser` is enabled with `displayGenColumns: false` to report memory allocation without showing garbage collection generation columns. The custom `Config` class is applied to the benchmark class to specify the jobs and summary style. - **Summary Style**: The summary output is configured to display ratios as percentages, making it easier to interpret how much faster or slower one runtime version is compared to another. ### Benchmark Method: `GetGenericTypeDefinition` - **Purpose**: This method benchmarks the performance of retrieving the generic type definition of a `Type` object. In this case, it's obtaining the generic type definition of `List<int>`. This operation is common in scenarios where generic type information is needed at runtime, such as in dependency injection frameworks, serialization libraries, or other reflection-based code. - **Performance Aspect**: The benchmark measures the time it takes to call `GetGenericTypeDefinition` on a `Type` object. This is an important performance aspect when working with generics via reflection because it can impact the overall performance of applications that rely heavily on reflection for dynamic type resolution. - **Expected Results/Insights**: By running this benchmark, you can gain insights into how efficiently the .NET runtime (specifically versions 7 and 8, as configured) handles the operation of retrieving generic type definitions via reflection. Faster execution times indicate better performance optimizations in the runtime or JIT compiler for this specific reflection operation. Comparing the results between .NET 7 and .NET 8 can reveal performance improvements or regressions introduced in the newer version of the .NET runtime. ### Conclusion This benchmark is tailored to developers and performance engineers interested in understanding the performance characteristics of reflection operations involving generics in .NET applications. It provides a focused view on one aspect of reflection performance, which can be critical for optimizing applications that make extensive use of reflection for dynamic type handling. The insights gained from such benchmarks can guide optimization efforts or influence the choice of .NET runtime version for specific applications.


Benchmark Comments: