Search code examples
c#performanceoptimizationstack

Why is Span faster than memory in 10 times


I have array of bytes

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

Results of benchmark

I can't understand why Span is faster than memory like in 10 times. From my perspective, Span is allocated on the stack but it points to the data on the heap (in my case). Memory is allocated on the heap and that's only one difference that I see here. My question is why Span is so fast? I have read about ref structs but didn't understand how it works.


Solution

  • The performance difference you are observing between Span and Memory is mainly due to the fact that Span is a stack-only data structure, while Memory can be used on the heap. This difference impacts the performance in various ways.

    1. Stack allocation: Since Span is a stack-only data structure, it benefits from the fast allocation and deallocation that the stack provides. Stack allocation is faster than heap allocation because it is simply a matter of adjusting the stack pointer, while heap allocation requires searching for an appropriately sized block of memory.
    2. No garbage collection: Stack-allocated objects, like Span, are automatically deallocated when they go out of scope, without requiring any garbage collection. On the other hand, Memory objects are subject to garbage collection, which introduces overhead and can impact performance.
    3. Ref struct: As you mentioned, Span is a ref struct. Ref structs cannot be stored on the heap and can only be passed by reference. This means that they can't be boxed or captured by closures, reducing the risk of unintended heap allocations and the associated performance overhead.

    However, it's important to note that the performance difference between Span and Memory is not always as significant as your benchmark results indicate. The relative performance may vary depending on the specific use case and the nature of the data being processed.

    In your particular example, the performance difference might be less about Span vs. Memory and more about the fact that you are using an asynchronous method (async Task) for the MemoryTest benchmark. Asynchronous methods introduce overhead due to state machines, and this overhead might be affecting the benchmark results. If you remove the async Task and use a regular synchronous method for the MemoryTest benchmark, the performance difference between SpanTest and MemoryTest might become less pronounced.