If I have a Stack, I would have expected that a mystack.Push() would be about the same performance for T being a struct, and maybe a little slower for T being a reference type. When I benchmark this scenario, it seems that a struct containing a reference type performs slightly worse than a struct containing a value. This despite both structs being the same size in memory (64 bits).
The benchmark was performed using .NET 6 and benchmarkdotnet.
Here are my test structs; note they should all be the same size on my x64 processor:
public readonly record struct RecStruct(long A);
public readonly record struct PtrStruct(UIntPtr A);
public readonly record struct StructRef(object Ref);
And here is my benchmarkdotnet project class
public class StructPerformance
{
private const int repeats = 1_000_000;
[Benchmark]
public Stack<RecStruct> PushStructWithCap()
{
var stack = new Stack<RecStruct>(repeats);
var strct = new RecStruct(0);
for (int i = 0; i < repeats; i++)
{
stack.Push(strct);
}
return stack;
}
[Benchmark]
public Stack<PtrStruct> PushPtrStructWithCap()
{
var stack = new Stack<PtrStruct>(repeats);
var ptr = new UIntPtr(0);
var strct = new PtrStruct(ptr);
for (int i = 0; i < repeats; i++)
{
stack.Push(strct);
}
return stack;
}
[Benchmark]
public Stack<StructRef> PushStructRefWithCap()
{
var stack = new Stack<StructRef>(repeats);
var obj = new object();
var strct = new StructRef(obj);
for (int i = 0; i < repeats; i++)
{
stack.Push(strct);
}
return stack;
}
[Benchmark]
public Stack<object> PushObjectWithCap()
{
var stack = new Stack<object>(repeats);
var obj = new object();
for (int i = 0; i < repeats; i++)
{
stack.Push(obj);
}
return stack;
}
}
My benchmark results are interesting, and seem to show that pushing a struct containing an object reference performs about as poorly as pushing an object, while an unmanaged structs perform better.
| Method | Mean | Error | StdDev | Median |
|---------------------- |---------:|----------:|----------:|---------:|
| PushStructWithCap | 2.605 ms | 0.0517 ms | 0.1315 ms | 2.583 ms |
| PushPtrStructWithCap | 2.586 ms | 0.0516 ms | 0.1121 ms | 2.558 ms |
| PushStructRefWithCap | 3.245 ms | 0.0643 ms | 0.1207 ms | 3.234 ms |
| PushObjectWithCap | 3.231 ms | 0.0630 ms | 0.1018 ms | 3.217 ms |
What is the reason for the extra ~ns/op on Stack.Push() for structs containing references?