You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

基准测试结果

MethodMeanErrorStdDevMedian
PushStructWithCap2.605 ms0.0517 ms0.1315 ms2.583 ms
PushPtrStructWithCap2.586 ms0.0516 ms0.1121 ms2.558 ms
PushStructRefWithCap3.245 ms0.0643 ms0.1207 ms3.234 ms
PushObjectWithCap3.231 ms0.0630 ms0.1018 ms3.217 ms

可以看到,包含对象引用的结构体入栈性能与直接入栈对象相近,均不如非托管结构体。


原因分析

核心原因是包含引用类型的结构体需要额外的写屏障(Write Barrier)操作,而非托管/纯值类型结构体不需要:

  • 当将包含引用的结构体写入Stack<T>的内部数组时,CLR必须执行写屏障来确保该引用被GC正确追踪,防止对象移动时丢失引用,这个操作会带来额外CPU开销。
  • 纯值类型结构体(如RecStruct、PtrStruct)的写入不涉及引用,CLR无需执行写屏障,直接完成内存拷贝即可,速度更快。
  • 直接入栈引用类型(object)时同样需要写屏障,因此性能和包含引用的结构体接近——两者本质都是在数组中存储引用,都会触发写屏障。

此外,JIT编译器对纯值类型结构体可生成更紧凑的内存拷贝代码,而包含引用的结构体因要处理写屏障,代码路径更长,无法完全优化掉额外的检查操作,进一步拉开了性能差距。


内容的提问来源于stack exchange,提问作者Alexander Flesher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 20:22:53