title: Performance using Span<T> to parse a text file
body: <p>I am trying to take advantage of <code>Span&lt;T&gt;</code>, using <strong>.NETCore 2.2</strong> to improve the performance of parsing text from a text file. The text file contains multiple consecutive rows of data which will each be split into fields that are then each mapped to a data class.</p>
  
  <p>Initially, the parsing routine uses a traditional approach of using <code>StreamReader</code> to read each row, and then using <code>Substring</code> to copy the individual fields from that row.</p>
  
  <p>From what I have read (<a href="https://msdn.microsoft.com/en-us/magazine/mt814808.aspx" rel="noreferrer">on MSDN</a>), amongst others, using <code>Span&lt;T&gt;</code> with <code>Slice</code> should perform more efficiently as less data allocations are made, and instead, a pointer to the <code>byte[]</code> array is passed around and acted upon.</p>
  
  <p>After some experimentation I have compared 3 approaches to parsing the file and used BenchmarkDotNet to compare the results. What I found was that, when parsing a single row from the text file using <code>Span</code>, both mean execution time and allocated memory are indeed significantly less. So far so good. However, when parsing more than one row from the file, the performance gain quickly disappears to the point that it is almost insignificant, even from as little as 50 rows.</p>
  
  <p><a href="https://i.stack.imgur.com/3CMQo.png" rel="noreferrer"><img src="https://i.stack.imgur.com/3CMQo.png" alt="benchmark results"></a></p>
  
  <p>I am sure I must be missing something. Something seems to be outweighing the performance gain of <code>Span</code>. </p>
  
  <p>The best performing approach <code>WithSpan_StringFirst</code> looks like this:</p>
  
  <pre><code>private static byte[] _filecontent;
  private const int ROWSIZE = 252;
  private readonly Encoding _encoding = Encoding.ASCII;
  
  public void WithSpan_StringFirst()
  {
      var buffer1 = new Span&lt;byte&gt;(_filecontent).Slice(0, RowCount * ROWSIZE);
      var buffer = _encoding.GetString(buffer1).AsSpan();
  
      int cursor = 0;
      for (int i = 0; i &lt; RowCount; i++)
      {
          var row = buffer.Slice(cursor, ROWSIZE);
          cursor += ROWSIZE;
          Foo.ReadWithSpan(row);
      }
  }
  
  [Params(1, 50)]
  public int RowCount { get; set; }
  </code></pre>
  
  <p>Implementation of <code>Foo.ReadWithSpan</code>:</p>
  
  <pre><code>public static Foo ReadWithSpan(ReadOnlySpan&lt;char&gt; buffer) =&gt; new Foo
  {
      Field1 = buffer.Read(0, 2),
      Field2 = buffer.Read(3, 4),
      Field3 = buffer.Read(5, 6),
      // ...
      Field30 = buffer.Read(246, 249)
  };
  
  public static string Read(this ReadOnlySpan&lt;char&gt; input, int startIndex, int endIndex)
  {
      return new string(input.Slice(startIndex, endIndex - startIndex));
  }
  </code></pre>
  
  <p>Any feedback would be appreciated. I have posted a <a href="https://github.com/Chris197/FileParseWithSpanBenchmark" rel="noreferrer">full working sample</a> on github.</p>