[Update: Span<T>] - Evitando alocações de memória ao ler arquivos textos
Com o release de hoje do Visual Studio 15.5, fiz uma versão utilizando Span<T>, ainda não é possível ver suas vantagens pois muitas APIs ainda não aceitam o ReadOnlySpan<char> no lugar de strings, o que permitiria por exemplo chamar o int.TryParse diretamente (Update 2: Agora permitem, o codigo foi atualizado para refletir isso). Com isso o Span<T> traria o melhor de todas as soluções apresentadas, a simplicidade do Substring, com a performance dos ponteiros, sem utilizar o unsafe.
Ainda assim o Span<T> mostra que sua performance foi a melhor entre os métodos avaliados, sem alocar praticamente nenhuma memória:
[Benchmark]
private long Read_ResultSpan(bool stack)
{
int bufferSize = GetLineSize();
Span buffer = stack ? stackalloc char[bufferSize] : new char[bufferSize];
long total = 0;
TextReader sr = GetReader();
{
while (sr.Read(buffer) > 0)
{
int lastIndex = 0;
for (int i = 0; i < positions.Length; i++)
{
ReadOnlySpan result = buffer.Slice(lastIndex, positions[i]);
lastIndex += positions[i];
total += int.Parse(result);
}
}
}
if (total != expected)
{
throw new Exception("Wrong result");
}
return total;
}
E o resultado final:
Method | Mean | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
---|---|---|---|---|---|---|
Minimum | 1.522 ms | 0.002 | - | - | - | 0 B |
ReadLine_ResultSubstring | 662.002 ms | 1.000 | 78000.0000 | - | - | 157 MB |
ReadLine_ResultCharArray | 649.625 ms | 0.981 | 78000.0000 | - | - | 157 MB |
Read_ResultCharArray | 443.691 ms | 0.670 | 57000.0000 | - | - | 114 MB |
Read_ResultCharArraySpan | 390.457 ms | 0.590 | - | - | - | 440 B |
Read_ResultUseUnsafeCharPointer | 551.917 ms | 0.834 | - | - | - | 1824 B |
Read_ResultSpanStackAlloc | 369.504 ms | 0.558 | - | - | - | 0 B |
Read_ResultSpanCharArray | 397.005 ms | 0.600 | - | - | - | 360 B |
Read_ResultSpanNative | 376.109 ms | 0.568 | - | - | - | 0 B |
O código está disponível em https://github.com/felipepessoto/AvoidingStringAllocation