Skip to content

.NET Core 实战 [No.198] 使用 Span<T> 提升处理字符串的性能

🏷️ 《.NET Core 实战》

Span<T> 用于操作各种 连续分布的内存数据 。可以通过以下来源初始化 Span<T> :

  • 常见的托管数组

    csharp
    Span<char> span = str.ToCharArray();
  • 栈内存中的对象

    csharp
    Span<int> arr = stackalloc [] {1, 2, 3};
  • 本地内存指针

    csharp
    // 从进程的未托管内存中分配 100 字节的内存
    IntPtr native = Marshal.AllocHGlobal(100);
    Span<byte> nativeSpan;
    unsafe
    {
        nativeSpan = new Span<byte>(native.ToPointer(), 100);
    }

Span<T> 支持 GC 功能,不需要显示释放内存。另外 ReadOnlySpan<T> 是其只读版本。

字符串的处理在 C# 和 Java 中都是类似的,每次字符串的修改都会创建一个新的实例,相当耗费资源。

这里通过一个示例,对比一下使用常规方法和使用 Span<T> 方法操作字符串时的性能。
示例功能:截取字符串中的字符 20 并转换为数值。

csharp
string str = "佳佳的博客 - 2020 - www.liujiajia.me";

Stopwatch sw1 = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
    int v = int.Parse(str.Substring(9, 2));
}
sw1.Stop();
Console.WriteLine($"常规方法:耗时 {sw1.ElapsedMilliseconds} ms");

Stopwatch sw2 = Stopwatch.StartNew();
ReadOnlySpan<char> span = str.ToCharArray();
for (int i = 0; i < 10000000; i++)
{
    int v = 0;
    var subSpan = span.Slice(9, 2);
    for (int j = 0; j < subSpan.Length; j++)
    {
        char ch = subSpan[j];
        v = (ch - '0') + v * 10;
    }
}
sw2.Stop();
Console.WriteLine($"使用 Span:耗时 {sw2.ElapsedMilliseconds} ms");

使用 str.Substring(9, 2) 方法截取字符串每次都会产生一个新的实例,而使用 span.Slice(9, 2) 则不会。

运行结果如下:


参考:《.NET Core 实战:手把手教你掌握 380 个精彩案例》 -- 周家安 著