提高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
下面说明了两种方法的性能差异。
正如你所看到的,使用 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. 编程宝库
ASP.NET提高StringBuilder类操作性能就向你介绍到这里,希望对你有所帮助。
来源:https://www.cnblogs.com/wanghao72214/p/15571181.html


猜你喜欢
- HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决
- 方法一:1.在pom.xml文件下添加依赖包<dependency><groupId>com.alibaba<
- 解决@NotBlank不生效在项目开发中,发现一个类中包含有另外一个类,这种包含关系的类上的@NotBlank校验不生效,后来发现需要在内部
- 1、满二叉树、完全二叉树、平衡二叉树、红黑树、二叉搜索树的区别?参考文章:树、二叉树(完全二叉树、满二叉树)概念图解① 满二叉树高度为&nb
- 1.1 简介 1.1.1 概述 Feign 旨在使编写 Java Http 客户端变得更容易。在使用 Ribbon + Rest
- 最近做了一个项目,其中有一个在线网页交流的需求,好久没写代码了,手都生疏了,于是先写demo练练手,分享到脚本之家平台,以此做个记录,方便自
- 接口:官方的含义是---->java接口是一系列方法的声明,是一些方法特征的集合疑问:那为什么不用抽象类呢?把他们共有的方法集合起来放
- 前提:① 已经提供了一个wsdl接口② 该接口能正常调用总体分为两种方式:1.使用cxf的wsdl2java工具生成本地类(使用方式就是本地
- 概述Handler是Android消息机制的上层接口。通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下,Hand
- 现在的智能手机不敢说百分百的都是触摸屏,也应该是百分之九九以上为触摸屏了,触摸屏为我们操作无键盘、无鼠标的手机系统带来了很多的便利。当用户触
- (1)用于对静态字段、只读字段等的初始化。
- 环境:springcloud Hoxton.SR11本节主要了解系统中的谓词与配置的路由信息是如何进行初始化关联生成路由对象的。每个谓词工厂
- 参考链接IDEA 2020.2.3版本IntelliJ IDEA 2020.2.3永久激活码(亲测有效)IDEA 2019.3版本Intel
- 多线程细节问题sleep方法和wait方法的异同点?相同点:让线程处于冻结状态.不同点:sleep必须指定时间 wait可以指定时间也可以不
- 一、绘制背景绘制背景的方法有两种:自己利用canvas进行绘制利用view的自带方法进行绘制1.1 canvas绘制背景自己绘制的背景的方法
- 实现步骤:工具:IDEA数据库版本:mysql5.7一、环境搭建1.创建springboot项目pom.xml2.pom.xml : spr
- 一、思路将分页所需的内容都放到一个实体类中分页数据所需要的实体类!内包含页码,页大小,总条数,总页数,起始行pagehelpr提供了这个类
- 本文主要是用到java中的swing技术,以及JMFjar中的API,为大家分享了java音乐播放器的具体实现代码,供大家参考,具体内容如下
- 一、cancel()无效当协程任务被取消的时候,它的内部是会产生一个 CancellationException 的。而协程的结构化并发,最
- spring 容器的创建对应 SpringApplication 中 run 中调用的 createApplicationContext 方