提高C# StringBuilder操作性能优化的方法

作者:编程宝库 时间:2023-04-26 01:16:13 

本文探讨使用C# StringBuilder 的最佳实践,用于减少内存分配,提高字符串操作的性能。

在 .NET 中,String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。

在需要对字符串执行重复修改的情况下,与创建新的 String 对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用 System.Text.StringBuilder 类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder 类可以提升性能。

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

下面说明了两种方法的性能差异。

提高C# StringBuilder操作性能优化的方法

正如你所看到的,使用 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 使用的内存明显较少。

提高C# StringBuilder操作性能优化的方法

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 方法中使用字符来追加字符。

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

提高C# StringBuilder操作性能优化的方法

6. 其他 StringBuilder 优化方法

StringBuilder 允许你设置容量以提高性能。如果你知道你要创建的字符串的大小,你可以相应地设置初始容量以大大减少内存分配。

你还可以通过使用一个可重复使用的 StringBuilder 对象池来避免分配来提高 StringBuilder 的性能。

最后,请注意,由于 StringBuilderCache是一个内部类,你需要将源代码粘贴到你的项目中才能使用它。回顾一下,在C#中你只能在同一个程序集或库中使用一个内部类。

因此,我们的程序文件不能仅仅通过引用 StringBuilderCache 所在的库来访问 StringBuilderCache 类。

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

参考资料:

1. C#教程

2. C#编程技术

3. 编程宝库

ASP.NET提高StringBuilder类操作性能就向你介绍到这里,希望对你有所帮助。

来源:https://www.cnblogs.com/wanghao72214/p/15571181.html

标签:C#,StringBuilder,性能
0
投稿

猜你喜欢

  • C#实现猜数字小游戏

    2023-01-13 16:39:38
  • C#仿QQ聊天窗口

    2022-09-30 09:12:39
  • Java实现分页的前台页面和后台代码

    2021-07-22 17:10:04
  • Spring Cache+Redis缓存数据的实现示例

    2023-11-26 11:53:20
  • C#语言async await工作原理示例解析

    2021-07-25 08:47:23
  • Android studio中生成引用.aar和.jar的方法详解

    2023-09-29 07:48:33
  • API处理Android安全距离详情

    2023-12-24 05:16:19
  • SpringMVC MVC架构与Servlet使用详解

    2023-10-10 15:25:02
  • Unity实现换装系统

    2021-08-11 15:27:15
  • OpenGL绘制三次Bezier曲线

    2022-04-23 18:18:14
  • java分页工具类的使用方法

    2023-08-17 02:00:14
  • WindowsForm实现警告消息框的实例代码

    2023-05-25 00:00:54
  • Java服务假死之生产事故的排查与优化问题

    2022-01-12 04:03:37
  • 浅谈Java中的Filter过滤器

    2023-07-23 10:00:08
  • 详解Android的内存优化--LruCache

    2022-07-18 14:28:41
  • Java获取e.printStackTrace()打印的信息方式

    2022-05-18 05:19:26
  • c# 实现轮询算法实例代码

    2023-01-30 03:48:51
  • java使用ftp上传文件示例分享

    2021-10-23 08:33:03
  • Java 导出Excel增加下拉框选项

    2021-10-13 07:58:50
  • 一篇文章带你初步认识Maven

    2023-08-31 01:56:14
  • asp之家 软件编程 m.aspxhome.com