使用 Span<T> 解析文本文件的性能

Chr*_*ris 8 c#

我正在尝试Span<T>利用.NETCore 2.2来提高从文本文件解析文本的性能。文本文件包含多个连续的数据行,每行将被拆分为字段,然后每个字段都映射到一个数据类。

最初,解析例程使用传统方法StreamReader读取每一行,然后使用Substring从该行复制各个字段。

从我读过的内容(在 MSDN 上)来看,除其他外,Span<T>随着Slice数据分配的减少,使用with应该更有效地执行,相反,一个指向byte[]数组的指针被传递并执行。

经过一些实验,我比较了 3 种解析文件的方法,并使用 BenchmarkDotNet 来比较结果。我发现,当使用 解析文本文件中的单行时Span,平均执行时间和分配的内存确实显着减少。到现在为止还挺好。但是,当从文件中解析多于一行时,性能提升很快就会消失,几乎可以忽略不计,即使是从 50 行开始也是如此。

基准结果

我确定我一定错过了一些东西。某些东西似乎超过了Span.

性能最佳的方法WithSpan_StringFirst如下所示:

private static byte[] _filecontent;
private const int ROWSIZE = 252;
private readonly Encoding _encoding = Encoding.ASCII;

public void WithSpan_StringFirst()
{
    var buffer1 = new Span<byte>(_filecontent).Slice(0, RowCount * ROWSIZE);
    var buffer = _encoding.GetString(buffer1).AsSpan();

    int cursor = 0;
    for (int i = 0; i < RowCount; i++)
    {
        var row = buffer.Slice(cursor, ROWSIZE);
        cursor += ROWSIZE;
        Foo.ReadWithSpan(row);
    }
}

[Params(1, 50)]
public int RowCount { get; set; }
Run Code Online (Sandbox Code Playgroud)

实施Foo.ReadWithSpan

public static Foo ReadWithSpan(ReadOnlySpan<char> buffer) => 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<char> input, int startIndex, int endIndex)
{
    return new string(input.Slice(startIndex, endIndex - startIndex));
}
Run Code Online (Sandbox Code Playgroud)

对于任何反馈,我们都表示感谢。我已经在 github 上发布了一个完整的工作示例

小智 -3

对于小于 10,000 行的小文件和要解析的简单行结构,大多数 .net Core 方法都是相同的。

对于大型、数 GB 文件和数百万行数据,优化更为重要。

如果文件处理时间为数小时甚至数十分钟,则将所有 C# 代码放在同一个类中将大大加快文件处理速度,因为编译器可以进行更好的代码优化。将调用的方法内联到主处理代码中也有帮助。

这与 20 世纪 60 年代的答案相同,改变处理算法及其对输入和输出数据进行分块的方式比小型代码优化要好一个数量级。