Hello there

My current technology stack: .NET 9, Python, TypeScript, and Azure.

I develop microservices and terraform of different sizes. Sharing my challenges and key learning.

About

The views expressed in this blog are my own and do not reflect my employer's. I am not responsible for any consequences of using the information provided. This blog is for educational purposes only, not for commercial use. Readers should apply their own judgment.

Span vs Memory vs ArraySegment vs ReadOnlySequence - Performance in .NET 9

January 01, 2025 Dipankar Haldar 30 people viewed this post

.NET 9 Memory Optimization: Span vs Memory vs ArraySegment vs ReadOnlySequence

.NET 9 introduces further optimizations for memory-efficient programming, making it essential to understand Span, Memory, ArraySegment, and ReadOnlySequence. These types help reduce heap allocations, improve performance, and optimize large data processing. In this blog, we’ll compare them through real-world use cases, benchmarks, and best practices.


Why Compare These Memory Types?

Efficient memory usage is crucial in performance-sensitive applications such as:

  • High-speed networking (e.g., processing large byte buffers)
  • File handling (e.g., reading/writing large datasets efficiently)
  • Streaming data processing (e.g., parsing logs, video/audio processing)
  • Zero-allocation performance (e.g., minimizing GC pressure in high-throughput systems)

Each of these types has specific advantages:

  • Span<T> – Fastest for stack-based, contiguous memory manipulation.
  • Memory<T> – Works across heap and stack, supporting async scenarios.
  • ArraySegment<T> – A lightweight wrapper for slicing arrays efficiently.
  • ReadOnlySequence<T> – Optimized for pipeline-based streaming data.

Key Performance Considerations

Feature Span Memory ArraySegment ReadOnlySequence
Heap Allocation ❌ No ✅ Yes (if heap-based) ✅ Yes ✅ Yes
Stack Allocation ✅ Yes ❌ No ❌ No ❌ No
Supports Async ❌ No ✅ Yes ❌ No ✅ Yes
Mutation Allowed ✅ Yes ✅ Yes ✅ Yes ❌ No (Read-Only)
Ideal for Slicing ✅ Yes ✅ Yes ✅ Yes ✅ Yes
Best for Pipelines ❌ No ❌ No ❌ No ✅ Yes

Benchmark Setup

We will compare Span, Memory, ArraySegment, and ReadOnlySequence for:

  1. String Manipulation & Slicing
  2. Large Dataset Iteration
  3. Buffer Processing Performance

Benchmark Code

public class MemoryOptimizationBenchmark
{
    private readonly byte[] _buffer = Enumerable.Repeat((byte)42, 1000000).ToArray();

    public void SpanSlice()
    {
        Span<byte> span = _buffer.AsSpan(500000, 500000);
        byte sum = 0;
        foreach (var item in span)
            sum += item;
    }
   
    public void MemorySlice()
    {
        Memory<byte> memory = _buffer.AsMemory(500000, 500000);
        byte sum = 0;
        foreach (var item in memory.Span)
            sum += item;
    }
   
    public void ArraySegmentSlice()
    {
        ArraySegment<byte> segment = new ArraySegment<byte>(_buffer, 500000, 500000);
        byte sum = 0;
        foreach (var item in segment)
            sum += item;
    }
   
    public void ReadOnlySequenceSlice()
    {
        ReadOnlySequence<byte> sequence = new ReadOnlySequence<byte>(_buffer);
        byte sum = 0;
        foreach (var memory in sequence)
        {
            foreach (var item in memory.Span)
                sum += item;
        }
    }
}

Test Environment

  • Processor: 13th Gen Intel Core i7-1370P, 1 CPU, 20 logical and 14 physical cores
  • .NET SDK: 9.0.103
  • Runtime: .NET 9.0.2 (9.0.225.6610), X64 RyuJIT AVX2

Benchmark Results

Method Mean Error StdDev Median Allocated
SpanSlice 205.0 us 1.53 us 3.60 us 204.1 us -
MemorySlice 167.5 us 1.60 us 1.50 us 167.2 us -
ArraySegmentSlice 267.5 us 29.21 us 86.12 us 215.9 us -
ReadOnlySequenceSlice 296.3 us 4.57 us 4.05 us 295.4 us -

Analysis & Key Takeaways

  1. Memory<T> outperforms Span<T> in this benchmark, likely due to optimizations in .NET 9.
  2. Span<T> remains a great choice for low-level, stack-allocated scenarios, but performance depends on the use case.
  3. ArraySegment<T> has high variance, making it less predictable in large-scale applications.
  4. ReadOnlySequence<T> is the slowest, but is designed for handling fragmented data streams, making it useful for streaming APIs and pipelines.

Best Practices

  • ✔️ Use Span<T> when performance is critical and data can fit in stack memory.
  • ✔️ Use Memory<T> for async-friendly memory handling.
  • ✔️ Use ArraySegment<T> when working with existing arrays.
  • ✔️ Use ReadOnlySequence<T> when handling large, pipeline-based data streams.
  • ✔️ Avoid unnecessary heap allocations by using Span wherever possible.

Conclusion

For memory-efficient applications in .NET 9:

  • Memory<T> performed best in this benchmark, showing its efficiency in heap-allocated memory.
  • Span<T> remains excellent for stack-allocated data, but may not always be the fastest.
  • ArraySegment<T> can be inconsistent due to runtime optimizations.
  • ReadOnlySequence<T> is slower but optimized for pipelines and streaming data.