C#提高StringBuilder操作性能优化的方法
本篇文章主要介绍使用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文件。
asp.net提高stringbuilder类操作性能就向你介绍到这里,希望对你有所帮助。