C#中含引用类型的结构体入栈为何比含值类型的结构体慢?
问题背景
我原本以为在C#中,当T为结构体时,Stack<T>.Push()的性能大致相同,T为引用类型时可能稍慢。但通过基准测试发现,包含引用类型的结构体入栈性能比包含值类型的结构体略差,尽管两者内存大小相同(64位)。
测试环境
本次基准测试基于.NET 6和BenchmarkDotNet进行。
测试用结构体(x64下内存大小均为64位)
public readonly record struct RecStruct(long A); public readonly record struct PtrStruct(UIntPtr A); public readonly record struct StructRef(object Ref);
基准测试代码
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; } }
基准测试结果
| 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 |
可以看到,包含对象引用的结构体入栈性能与直接入栈对象相近,均不如非托管结构体。
原因分析
核心原因是包含引用类型的结构体需要额外的写屏障(Write Barrier)操作,而非托管/纯值类型结构体不需要:
- 当将包含引用的结构体写入
Stack<T>的内部数组时,CLR必须执行写屏障来确保该引用被GC正确追踪,防止对象移动时丢失引用,这个操作会带来额外CPU开销。 - 纯值类型结构体(如
RecStruct、PtrStruct)的写入不涉及引用,CLR无需执行写屏障,直接完成内存拷贝即可,速度更快。 - 直接入栈引用类型(
object)时同样需要写屏障,因此性能和包含引用的结构体接近——两者本质都是在数组中存储引用,都会触发写屏障。
此外,JIT编译器对纯值类型结构体可生成更紧凑的内存拷贝代码,而包含引用的结构体因要处理写屏障,代码路径更长,无法完全优化掉额外的检查操作,进一步拉开了性能差距。
内容的提问来源于stack exchange,提问作者Alexander Flesher
相关产品推荐
相关产品推荐

