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

为何Span性能比Memory高出10倍?附基准测试代码与结果

为何Span的性能比Memory高出10倍?

基准测试代码

private byte[] _data;

[Benchmark()]
public void SpanTest()
{
    var temp = _data.AsSpan();
    var length = BitConverter.ToInt32(temp.Slice(0, 4));
    var buffData = temp.Slice(4, length);
    var s = buffData[0];
}

[Benchmark()]
public async Task MemoryTest()
{
    var temp = _data.AsMemory();
    var length = BitConverter.ToInt32(temp.Slice(0, 4).Span);
    var buffData = temp.Slice(4, length);
    var s = buffData.Span[0];
}

基准测试结果

基准测试结果

问题

我无法理解为何Span的性能比Memory高出10倍。在我看来,Span分配在栈上但指向堆中的数据(我的场景中),而Memory分配在堆上,这是我能看到的唯一区别。我的问题是:为何Span如此之快?我了解过ref struct,但不明白它的工作原理。

解答

1. Span是栈分配的ref struct,无堆开销

Span是ref struct类型,它的核心数据(指向内存的指针、偏移量、长度)直接存储在栈帧中,完全没有堆内存分配。而Memory是引用类型,从数组转换为Memory时会在堆上创建实例,每次调用Slice也会生成新的堆对象——这些堆分配和实例化本身就会带来额外性能损耗。

2. 避免间接寻址的额外开销

访问Memory内容必须先获取.Span属性,这一步需要从堆上的Memory实例读取内存范围信息,相当于多了一次堆内存的间接访问。而Span本身在栈上,所有操作(Slice、索引访问)直接基于栈上的指针和偏移量计算,不需要额外堆内存读写,减少了寻址损耗。

3. 编译器的特殊优化

ref struct有严格约束(不能装箱、不能被捕获到闭包、不能跨await/async边界),编译器可以对Span做更激进的优化:比如将Span的字段直接内联到调用栈中,消除不必要的对象操作;甚至直接编译为底层指针运算,进一步提升执行效率。

回到你的测试代码:

  • SpanTest全程在栈上完成:AsSpan()生成栈上Span,Slice仅修改栈上的偏移和长度,索引访问直接通过栈指针计算内存地址,无堆交互。
  • MemoryTest中AsMemory()和两次Slice都会创建堆对象,每次访问.Span都要从堆读取数据,这些额外步骤叠加后,就造成了10倍的性能差距。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:52:25