1
Object Creation Performance Analysis
Date Added (UTC):
24 Apr 2024 @ 09:22
Date Updated (UTC):24 Apr 2024 @ 09:22
.NET Version(s): Tag(s):
Added By:
A dedicated executive technical architect who is focused on expanding organizations technology capabilities.
Benchmark Results:
Benchmark Code:
Originally imported from :
https://gist.github.com/admir-live/c773b8cdf1d3d4eebeae05b92e365bf2on 24 Apr 2024 @ 09:22 (UTC) .
The original benchmark may have changed.
https://gist.github.com/admir-live/c773b8cdf1d3d4eebeae05b92e365bf2
The original benchmark may have changed.
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using ObjectCreationBenchmarks;
namespace ObjectCreationBenchmarks;
public sealed class Person
{
}
[MemoryDiagnoser]
public class Benchmarks
{
[Benchmark(Baseline = true)]
public Person NewKeyword()
=> new Person();
[Benchmark]
public object ActivatorCreateInstanceWithTypeOf()
=> Activator.CreateInstance(typeof(Person));
[Benchmark]
public Person ActivatorCreateInstanceGeneric()
=> Activator.CreateInstance<Person>();
[Benchmark]
public object GetUninitializedObject()
=> RuntimeHelpers.GetUninitializedObject(typeof(Person));
}
public class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmarks>();
}
}
Powered by SharpLab
// .NET 8
public Person NewKeyword()
{
return new Person();
}
// .NET 8
public object ActivatorCreateInstanceWithTypeOf()
{
return Activator.CreateInstance(typeof(Person));
}
// .NET 8
public Person ActivatorCreateInstanceGeneric()
{
return Activator.CreateInstance<Person>();
}
// .NET 8
public object GetUninitializedObject()
{
return RuntimeHelpers.GetUninitializedObject(typeof(Person));
}
Powered by SharpLab
// .NET 8
.method public hidebysig
instance class ObjectCreationBenchmarks.Person NewKeyword () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 1a 00 00 00 01 5f 01 00 54 02 08 42 61 73
65 6c 69 6e 65 01
)
// Method begins at RVA 0x2058
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 28, col 12) to (line 28, col 24) in _
IL_0000: newobj instance void ObjectCreationBenchmarks.Person::.ctor()
IL_0005: ret
}
// .NET 8
.method public hidebysig
instance object ActivatorCreateInstanceWithTypeOf () 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 0x205f
// Code size 16 (0x10)
.maxstack 8
// sequence point: (line 32, col 12) to (line 32, col 52) in _
IL_0000: ldtoken ObjectCreationBenchmarks.Person
IL_0005: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_000a: call object [System.Runtime]System.Activator::CreateInstance(class [System.Runtime]System.Type)
IL_000f: ret
}
// .NET 8
.method public hidebysig
instance class ObjectCreationBenchmarks.Person ActivatorCreateInstanceGeneric () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 22 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2070
// Code size 6 (0x6)
.maxstack 8
// sequence point: (line 36, col 12) to (line 36, col 46) in _
IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance<class ObjectCreationBenchmarks.Person>()
IL_0005: ret
}
// .NET 8
.method public hidebysig
instance object GetUninitializedObject () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 26 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2077
// Code size 16 (0x10)
.maxstack 8
// sequence point: (line 40, col 12) to (line 40, col 65) in _
IL_0000: ldtoken ObjectCreationBenchmarks.Person
IL_0005: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_000a: call object [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::GetUninitializedObject(class [System.Runtime]System.Type)
IL_000f: ret
}
Powered by SharpLab
|
|
|
|
Benchmark Description:
In this analysis, we delve into the performance of different methods for creating objects in .NET, utilizing the benchmarking tool BenchmarkDotNet. Our goal is to understand how each method impacts performance and when to use each technique.
##### Environment Setup
Runtime: .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
Garbage Collector: Concurrent Workstation
System: Windows 11, AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores
.NET SDK: 8.0.202
The provided benchmark code is designed to compare different methods of creating instances of an object in .NET. It uses the BenchmarkDotNet library, a powerful tool for benchmarking .NET code, to measure and compare the performance of these methods. The benchmarks are focused on the `Person` class, which is an empty class in this context. The .NET version isn't explicitly mentioned, but given the methods used, it should be compatible with .NET Core 2.0 and above, including .NET 5 and .NET 6.
### General Setup
- **BenchmarkDotNet Attributes**: The `[MemoryDiagnoser]` attribute is used to enable memory diagnostics, allowing the benchmark to report on allocations.
- **.NET Version**: Not specified, but the methods used are available in .NET Core 2.0 and later versions.
- **Configuration**: The `BenchmarkRunner.Run<Benchmarks>()` in the `Main` method is the entry point for running the benchmarks. BenchmarkDotNet will handle the execution and measurement.
### Benchmark Methods
#### 1. `NewKeyword()`
- **Purpose**: This method benchmarks the direct instantiation of an object using the `new` keyword.
- **Performance Aspect**: It measures the speed and memory allocations involved in the most common way of creating an object instance.
- **Importance**: This serves as the baseline for comparison because it's the standard and most straightforward way to create an object in .NET.
- **Expected Results**: This method is expected to be the fastest and least memory-intensive way to create an object, serving as a baseline for other methods.
#### 2. `ActivatorCreateInstanceWithTypeOf()`
- **Purpose**: This method tests the performance of creating an instance using `Activator.CreateInstance` with a `Type` parameter.
- **Performance Aspect**: It measures how reflection-based creation compares to the direct `new` keyword in terms of speed and memory allocations.
- **Importance**: Reflection is powerful but known to be slower than direct instantiation. Understanding the performance impact is crucial for scenarios where dynamic type creation is necessary.
- **Expected Results**: This method is likely to be slower and more memory-intensive than using the `new` keyword, reflecting the overhead of reflection.
#### 3. `ActivatorCreateInstanceGeneric()`
- **Purpose**: This benchmarks the generic version of `Activator.CreateInstance`, which avoids using the `Type` parameter.
- **Performance Aspect**: It evaluates the performance differences between generic reflection and non-generic reflection or direct instantiation.
- **Importance**: The generic method could potentially offer performance benefits over its non-generic counterpart by avoiding some runtime type checks.
- **Expected Results**: This method might perform better than `Activator.CreateInstance` with a `Type` parameter but still not as good as the direct `new` keyword due to the overhead of reflection.
#### 4. `GetUninitializedObject()`
- **Purpose**: This method benchmarks the creation of an uninitialized object using `RuntimeHelpers.GetUninitializedObject`.
- **Performance Aspect**: It measures the performance of creating an object without calling its constructor.
- **Importance**: This approach is rarely used but can be important in serialization, deserialization, and scenarios requiring a high level of control over object instantiation.
- **Expected Results**: This method might be faster than reflection-based methods but can lead to objects in an invalid state if not used carefully. It's a specialized case and might show lower memory allocations since constructors are not called.
### Summary
Running these benchmarks provides insights into the trade-offs between different object instantiation methods in .NET. The direct `new` keyword is expected to be the most efficient, serving as a baseline. Reflection-based methods (`Activator.CreateInstance`) offer flexibility at the cost of performance. `GetUninitializedObject` is a specialized tool that trades constructor guarantees for potentially faster instantiation. Understanding these trade-offs is crucial for optimizing performance-critical paths in .NET applications.