关于c#:如何提高C-StringBuilder的性能

40次阅读

共计 4646 个字符,预计需要花费 12 分钟才能阅读完成。

本文探讨应用 C# StringBuilder 的最佳实际,用于缩小内存调配,进步字符串操作的性能。

在 .NET 中,字符串是不可变的类型。每当你在 .NET 中批改一个字符串对象时,就会在内存中创立一个新的字符串对象来保留新的数据。相比之下,StringBuilder 对象代表了一个可变的字符串,并随着字符串大小的增长动静地扩大其内存调配。

String 和 StringBuilder 类是你在 .NET Framework 和 .NET Core 中解决字符串时常常应用的两个风行类。然而,每个类都有其长处和毛病。

BenchmarkDotNet 是一个轻量级的开源库,用于对 .NET 代码进行基准测试。BenchmarkDotNet 能够将你的办法转化为基准,跟踪这些办法,而后提供对捕捉的性能数据的洞察力。在这篇文章中,咱们将利用 BenchmarkDotNet 为咱们的 StringBuilder 操作进行基准测试。

要应用本文提供的代码示例,你的零碎中应该装置有 Visual Studio 2019 或者以上版本。

1. 在 Visual Studio 中创立一个控制台应用程序我的项目

首先让咱们在 Visual Studio 中 创立一个 .NET Core 控制台应用程序我的项目。假如你的零碎中曾经装置了 Visual Studio 2019,请依照上面的步骤创立一个新的 .NET Core 控制台应用程序我的项目。

  1. 启动 Visual Studio IDE。
  2. 点击 “ 创立新我的项目 ”。
  3. 在 “ 创立新我的项目 “ 窗口中,从显示的模板列表中抉择 “ 控制台应用程序(.NET 外围)”。
  4. 点击 “ 下一步 ”。
  5. 在接下来显示的 “ 配置你的新我的项目 “ 窗口中,指定新我的项目的名称和地位。
  6. 点击创立。

这将在 Visual Studio 2019 中创立一个新的 .NET Core 控制台应用程序我的项目。咱们将在本文的后续章节中应用这个我的项目来解决 StringBuilder。

2. 装置 BenchmarkDotNet NuGet 包

要应用 BenchmarkDotNet,你必须装置 BenchmarkDotNet 软件包。你能够通过 Visual Studio 2019 IDE 内的 NuGet 软件包管理器,或在 NuGet 软件包管理器控制台执行以下命令来实现。

Install-Package BenchmarkDotNet

3. 应用 StringBuilderCache 来缩小调配

StringBuilderCache 是一个外部类,在 .NET 和 .NET Core 中可用。每当你须要创立多个 StringBuilder 的实例时,你能够应用 StringBuilderCache 来大大减少调配的老本。

StringBuilderCache 的工作原理是缓存一个 StringBuilder 实例,而后在须要一个新的 StringBuilder 实例时从新应用它。这缩小了调配,因为你只须要在内存中领有一个 StringBuilder 实例。

让咱们用一些代码来阐明这一点。在 Program.cs 文件中创立一个名为 StringBuilderBenchmarkDemo 的类。创立一个名为 AppendStringUsingStringBuilder 的办法,代码如下。

public string AppendStringUsingStringBuilder()
{var stringBuilder = new StringBuilder();
    stringBuilder.Append("First String");
    stringBuilder.Append("Second String");
    stringBuilder.Append("Third String");
    return stringBuilder.ToString();}

下面的代码片段显示了如何应用 StringBuilder 对象来追加字符串。接下来创立一个名为 AppendStringUsingStringBuilderCache 的办法,代码如下。

public string AppendStringUsingStringBuilderCache()
{var stringBuilder = StringBuilderCache.Acquire();
    stringBuilder.Append("First String");
    stringBuilder.Append("Second String");
    stringBuilder.Append("Third String");
    return StringBuilderCache.GetStringAndRelease(stringBuilder);
}

下面的代码片段阐明了如何应用 StringBuilderCache 类的 Acquire 办法创立一个 StringBuilder 实例,而后用它来追加字符串。

上面是 StringBuilderBenchmarkDemo 类的残缺源代码供你参考。

[MemoryDiagnoser]
public class StringBuilderBenchmarkDemo {[Benchmark]
      public string AppendStringUsingStringBuilder() {var stringBuilder = new StringBuilder();
            stringBuilder.Append("First String");
            stringBuilder.Append("Second String");
            stringBuilder.Append("Third String");
            return stringBuilder.ToString();}
      [Benchmark]
      public string AppendStringUsingStringBuilderCache() {var stringBuilder = StringBuilderCache.Acquire();
            stringBuilder.Append("First String");
            stringBuilder.Append("Second String");
            stringBuilder.Append("Third String");
            return StringBuilderCache.GetStringAndRelease(stringBuilder);
      }
}

你当初必须应用 BenchmarkRunner 类来指定初始终点。这是一种告诉 BenchmarkDotNet 在指定的类上运行基准的形式。

用以下代码替换 Main 办法的默认源代码。

static void Main(string[] args)
{var summary = BenchmarkRunner.Run<StringBuilderBenchmarkDemo>();
}

当初在 Release 模式下编译你的我的项目,并在命令行应用以下命令运行基准测试。

dotnet run -p StringBuilderPerfDemo.csproj -c Release

上面阐明了两种办法的性能差别。

正如你所看到的,应用 StringBuilderCache 追加字符串要快得多,须要的调配也少。

4. 应用 StringBuilder.AppendJoin 而不是 String.Join

String 对象是不可变的,所以批改一个 String 对象须要创立一个新的 String 对象。因而,在连贯字符串时,你应该应用 StringBuilder.AppendJoin 办法,而不是 String.Join,以缩小调配,进步性能。

上面的代码列表阐明了如何应用 String.Join 和 StringBuilder.AppendJoin 办法来组装一个长字符串。

[Benchmark]
public string UsingStringJoin() {
   var list = new List < string > {
                  "A",
                  "B", "C", "D", "E"
      };
      var stringBuilder = new StringBuilder();
      for (int i = 0; i < 10000; i++) {stringBuilder.Append(string.Join(' ', list));
      }
      return stringBuilder.ToString();}
[Benchmark]
public string UsingAppendJoin() {
    var list = new List < string > {
                "A",
                "B", "C", "D", "E"
    };
    var stringBuilder = new StringBuilder();
    for (int i = 0; i < 10000; i++) {stringBuilder.AppendJoin(' ', list);
    }
    return stringBuilder.ToString();}

下图显示了这两种办法的基准测试后果。

请留神,对于这个操作,这两种办法的速度很靠近,但 StringBuilder.AppendJoin 应用的内存显著较少。

5. 应用 StringBuilder 追加单个字符

留神,在应用 StringBuilder 时,如果须要追加单个字符,应该应用 Append(char) 而不是 Append(String)。

请思考以下两个办法。

[Benchmark]
public string AppendStringUsingString() {var stringBuilder = new StringBuilder();
      for (int i = 0; i < 1000; i++) {stringBuilder.Append("a");
            stringBuilder.Append("b");
            stringBuilder.Append("c");
      }
      return stringBuilder.ToString();}
[Benchmark]
public string AppendStringUsingChar() {var stringBuilder = new StringBuilder();
      for (int i = 0; i < 1000; i++) {stringBuilder.Append('a');
            stringBuilder.Append('b');
            stringBuilder.Append('c');
      }
      return stringBuilder.ToString();}

从名字中就能够看出,AppendStringUsingString 办法阐明了如何应用一个字符串作为 Append 办法的参数来追加字符串。

AppendStringUsingChar 办法阐明了你如何在 Append 办法中应用字符来追加字符。

下图显示了这两种办法的基准测试后果。

6. 其余 StringBuilder 优化办法

StringBuilder 容许你设置容量以进步性能。如果你晓得你要创立的字符串的大小,你能够相应地设置初始容量以大大减少内存调配。

你还能够通过应用一个可重复使用的 StringBuilder 对象池来防止调配来进步 StringBuilder 的性能。

最初,请留神,因为 StringBuilderCache 是一个外部类,你须要将源代码粘贴到你的我的项目中能力应用它。回顾一下,在 C# 中你只能在同一个程序集或库中应用一个外部类。

因而,咱们的程序文件不能仅仅通过援用 StringBuilderCache 所在的库来拜访 StringBuilderCache 类。

这就是为什么咱们把 StringBuilderCache 类的源代码复制到咱们的程序文件中,也就是 Program.cs 文件。

参考资料:

  1. C# 教程
  2. C# 编程技术
  3. 编程宝库

正文完
 0