Nouman Rahman
ProgrammingFire

ProgrammingFire

What Is Span In C#? And How It Improves The Performance?

What Is Span In C#? And How It Improves The Performance?

Nouman Rahman's photo
Nouman Rahman
·Mar 24, 2022·

3 min read

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Table of contents

  • What Is Span?
  • Example
  • Benchmarks
  • How Does Substring Works?
  • How Does Span Works?

What Is Span?

Span is a ref struct in c# that can help you save a lot of memory allocation. Because span is ref struct it can be only allocated on the stack and not on the heap which means it does not require garbage collection which effectively means that there gonna be no pause in your application for garbage collection. Let's see what span is and how you can use it with an example:

Example

In this example, we need to get extract the day, month, year from a string that is formatted in a specific way. For that, we can use the Substring method.

With Substring

var dateAsText = "03 02 2022";

// Create A Tuple With (Day, Month, Year) In It.
(int day, int month, int year) DateAsTupleWithSubstring()
{
    var dayAsText = dateAsText.Substring(0, 2);
    var monthAsText = dateAsText.Substring(3, 2);
    var yearAsText = dateAsText.Substring(6);
    var day = int.Parse(dayAsText);
    var month = int.Parse(monthAsText);
    var year = int.Parse(yearAsText);
    return (day, month, year);
}

It looks just right but here is a problem that this is allocating a string on the heap each time we use the Substring method which is expensive because then the garbage collector needs to go and do some garbage collection which effectively makes your application pause. We can fix this issue using a ReadOnlySpan.

With Span

var dateAsText = "03 02 2022";

// Create a tuple with (Day, Month, Year) in it.
(int day, int month, int year) DateAsTupleWithSpan()
{
    ReadOnlySpan<char> dateAsSpan = dateAsText;
    var dayAsText = dateAsSpan.Slice(0, 2);
    var monthAsText = dateAsSpan.Slice(3, 2);
    var yearAsText = dateAsSpan.Slice(6);
    var day = int.Parse(dayAsText);
    var month = int.Parse(monthAsText);
    var year = int.Parse(yearAsText);
    return (day, month, year);
}

This code actually performs better than the previous one we will talk about how it does that after benchmarking both functions let's do some benchmarking now with the Benchmark.NET library. If you need to know how to use the Benchmark.NET library to Benchmark.NET code read this blog post: Benchmark Your .NET Code With Benchmark.NET

Benchmarks

Defining Benchmarks

using BenchmarkDotNet.Attributes;

[MemoryDiagnoser]
public class SpanVsSubstring
{
    private readonly string dateAsText = "03 02 2022";

    [Benchmark]
    // Create A Tuple With (Day, Month, Year) In It.
    public (int day, int month, int year) DateAsTupleWithSubstring()
    {
        var dayAsText = dateAsText.Substring(0, 2);
        var monthAsText = dateAsText.Substring(3, 2);
        var yearAsText = dateAsText.Substring(6);
        var day = int.Parse(dayAsText);
        var month = int.Parse(monthAsText);
        var year = int.Parse(yearAsText);
        return (day, month, year);
    }

    [Benchmark]
    public (int day, int month, int year) DateAsTupleWithSpan()
    {
        ReadOnlySpan<char> dateAsSpan = dateAsText;
        var dayAsText = dateAsSpan.Slice(0, 2);
        var monthAsText = dateAsSpan.Slice(3, 2);
        var yearAsText = dateAsSpan.Slice(6);
        var day = int.Parse(dayAsText);
        var month = int.Parse(monthAsText);
        var year = int.Parse(yearAsText);
        return (day, month, year);
    }
}

Benchmark Results

MethodMeanErrorStdDevMedianGen 0Allocated
DateAsTupleWithSubstring143.13 ns8.278 ns24.147 ns134.34 ns0.091896 B
DateAsTupleWithSpan69.98 ns2.363 ns6.780 ns68.32 ns--

As you can see the DateAsTupleWithSpan function is more than half faster than the DateAsTupleWithSubstring one which is a big performance improvement but the interesting thing that happens on the memory allocation which you can see the span did not allocate even a single byte on the heap where the substring one allocated 96B on the heap which then needs to be garbage collected through the garbage collector which again pauses your application. But really how span did the same thing that substring did but without allocating even a single byte on the heap. Here's a diagram that shows this.

How Does Substring Works?

HowSpanWorks.dio-Substring.drawio.png

How Does Span Works?

HowSpanWorks.dio-Span.drawio.png

Did you find this article valuable?

Support Nouman Rahman by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this