不一样Framework下StringBuilder和String的性能对比,及不一样Framework性能比(附Demo)

本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文连接,谢谢合做。git

文章是哥(mephisto)写的,SourceLinkgithub

阅读目录

本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文连接,谢谢合做。web

文章是哥(mephisto)写的,SourceLink面试

 

介绍

    本身对String和StringBuilder的处理机制略懂,大胆的设想下二者的性能对比会出现什么样的使人意外的状况。因而便有此文,结果我也不知道,那么咱们就根据设计的测试用例来看看现象吧。本文耗时5个小时(包括测试用例),但愿你们给点汗水的推荐。算法

环境搭建

一:搭建多Framework测试解决方案

  因为要测试多个Framework下的性能,因此就建立了个解决方案,版本包括:2.0,3.0,3.5,4.0,4.5。这么多的工程,测试起来也比较麻烦,俺仍是耐心的进行了测试。app

  IDE采用的VS2013,因此你们要下载后本身运行Demo,仍是要注意下IDE版本,如是低版本,本身将4.5的移除便可dom

二:主要类及方法介绍

1:TestUtil类分布式

  用于String和StringBuilder的建立的工具类ide

  1.1:CreateString()工具

  建立String方法

 1         public string CreateString(int num, string value)
 2  { 3 string temp = string.Empty; 4 while (num > 0) 5  { 6 temp += value; 7 num--; 8  } 9 10 return temp; 11 }

  1.2:CreateStringBuilder()

  建立StringBuilder方法

 1         public string CreateStringBuilder(int num, string value)
 2  { 3 StringBuilder sb = new StringBuilder(); 4 while (num > 0) 5  { 6  sb.Append(value); 7 num--; 8  } 9 10 return sb.ToString(); 11 }

  2:Test类

  2.1:RunStringMinInF2()

  运行String最小次数测试

 1         private static readonly int runCount = 10000000;
 2         public static void RunStringMinInF2(TestUtil testUtil, string word) 3  { 4 DateTime date1 = DateTime.Now; 5 6 int i = runCount * 5; 7 while (i > 0) 8  { 9 testUtil.CreateString(2, word); 10 i--; 11  } 12 DateTime date2 = DateTime.Now; 13 Console.WriteLine("stringMin:" + (date2 - date1).TotalMilliseconds.ToString()); 14 }

  2.2:RunSBMinInF2()

  运行StringBuilder最小次数测试

 1         public static void RunSBMinInF2(TestUtil testUtil, string word)
 2         {
 3             DateTime date1 = DateTime.Now;
 4 
 5             int i = runCount * 5;
 6             while (i > 0)
 7             {
 8                 testUtil.CreateStringBuilder(2, word);
 9                 i--;
10             }
11             DateTime date2 = DateTime.Now;
12             Console.WriteLine("stringbuilderMin:" + (date2 - date1).TotalMilliseconds.ToString());
13         }

  2.3:RunString10InF2()

  运行单个String实体调用10次相加测试

 1         public static void RunString10InF2(TestUtil testUtil, string word)
 2         {
 3             DateTime date1 = DateTime.Now;
 4 
 5             int i = runCount;
 6             while (i > 0)
 7             {
 8                 testUtil.CreateString(10, word);
 9                 i--;
10             }
11             DateTime date2 = DateTime.Now;
12             Console.WriteLine("string10:" + (date2 - date1).TotalMilliseconds.ToString());
13         }

  2.4:RunSB10InF2()

  运行单个StringBuilder实体调用10次相加测试

 1         public static void RunSB10InF2(TestUtil testUtil, string word)
 2         {
 3             DateTime date1 = DateTime.Now;
 4 
 5             int i = runCount;
 6             while (i > 0)
 7             {
 8                 testUtil.CreateStringBuilder(10, word);
 9                 i--;
10             }
11             DateTime date2 = DateTime.Now;
12             Console.WriteLine("stringbuilder10:" + (date2 - date1).TotalMilliseconds.ToString());
13         }

测试用例

一:在Framework2.0下最少字段数相加

  测试字符串:“a”

  单个实例测试数据次数:2

  被调用总次数:10000000

  测试代码以下:

 1     class Program
 2  { 3 static void Main(string[] args) 4  { 5 TestUtil testUtil = new TestUtil(); 6 7 Test.RunStringMinInF2(testUtil, "a"); 8 Test.RunSBMinInF2(testUtil, "a"); 9 Test.RunSBMinInF2(testUtil, "a"); 10 Test.RunStringMinInF2(testUtil, "a"); 11 12  Console.Read(); 13  } 14 }

  防止因为前后调用缘由,因此测试代码中同一个方法出现2次。屡次运行,基本上结果差很少,同一次运行中也有数据相差的,但差异很小(几十ms)。

  运行截图以下:

  

  结果中在这样极端的考验StringBuilder性能的条件下,StringBuilder果真败下阵来。

 二:在Framework2.0下最少字段数相加(字符串增长)

  测试字符串:“abcdefghijklmn1234567890opqrst~!@#^&*()”

  其余测试步骤:同测试方案一

  测试代码以下:

 1         static void Main(string[] args)
 2  { 3 TestUtil testUtil = new TestUtil(); 4 5 Test.RunStringMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 6 Test.RunSBMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 7 Test.RunSBMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 8 Test.RunStringMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 9 10  Console.Read(); 11 }

  运行截图以下:

  

  很显然字符串增长了,对二者的处理时间都增长了,StringBuilder仍是必然的败在阵来。

  三:在多Framework下最少字段数相加

  其余测试步骤:同测试方案一

  测试结果以下:

  

  发如今4.0和4.5的版本中StringBuilder是比原来的版本有必定优点的,在StringBuilder上4.0和4.5优点都很大,在String上4.5较其余版本略微有点优点。看来微软也是在进步啊,你们是否是很期待开源了的Framework,不少牛人能够对其进行贡献,那性能不是又进步了。

  四:在多Framework下最少字段数相加(字符串增长)

  其余测试步骤:同测试方案二

  测试结果以下:

  

  几个Framework版本结果差很少,可是在StringBuilder上4.0和4.5败在阵来,这是为何列,你们思考一下,稍会做答

  五:在Framework2.0下10字段数相加

  测试字符串:“a”

  单个实例测试数据次数:10

  被调用总次数:10000000

  测试代码以下:

 1     class Program
 2  { 3 static void Main(string[] args) 4  { 5 TestUtil testUtil = new TestUtil(); 6 7 Test.RunString10InF2(testUtil, "a"); 8 Test.RunSB10InF2(testUtil, "a"); 9 Test.RunSB10InF2(testUtil, "a"); 10 Test.RunString10InF2(testUtil, "a"); 11 12  Console.Read(); 13  } 14 }

  测试结果以下:

  对单个实例调用次数增长,StringBuilder的优点就来了,渐渐的String就只能当吊车尾了。

  六:在Framework2.0下10字段数相加(字符串增长)

  测试字符串:“abcdefghijklmn1234567890opqrst~!@#^&*()”

  其余测试步骤:同测试方案五

  测试结果以下:

  这个测试很出乎意料啊,跟测试方案二,string的性能差异这么多,StringBuilder没什么差异,你们也能够思考下。

  

  七:在多Framework下10字段数相加

  其余测试步骤:同测试方案五

  测试结果以下:

  

  在众多Framework中,4.5和4.0拖妥妥的排在前2位。

  

  八:在多Framework下10字段数相加(字符串增长)

  其余测试步骤:同测试方案六

  测试结果以下:

  

  运行性能都差很少,区别不大。

MSDN说明

一:String和 StringBuilder的类型

  虽然 StringBuilder 和 String 两个表示字符序列,但它们以不一样的方式实现。  String   是不可变的类型。  即看似修改 String 对象的每一个操做实际建立新的字符串。

  对执行普遍的字符串操做实例 (如修改循环中的字符串) ,修改字符串能重复精确严重的性能损失。  方法是使用 StringBuilder,它是易失的字符串选件类。  可变性意味着,一旦选件类的实例建立的,则能够将其追加,取消,替换或插入字符修改。  StringBuilder   对象维护缓冲区容纳扩展到该字符串。  若是有足够的空间,新数据将被追加到缓冲区;不然,将分配一个新的、更大的缓冲区,原始缓冲区中的数据被复制到新的缓冲区,而后将新数据追加到新的缓冲区。

  虽然 StringBuilder 选件类比 String 选件类一般提供更好的性能,当不该使用 StringBuilder 自动替换 String 时,就要操做字符串。  性能取决于该字符串的大小,对新的字符串将分配的内存量,您的系统 app 的执行和操做的类型。  您应准备测试您的应用程序肯定 StringBuilder 其实是否可显着提升性能。

  

在这些条件下考虑使用 String 选件类:

  • 当您的应用程序将对字符串更改的数量很小。  在这些状况下,StringBuilder 不可能提供在 String的忽略或性能改进。 

  • 当执行串联运算的内置的数字,尤为是对于字符串文本。  在这种状况下,编译器可能将串联运算到单个操做。 

  • 当您生成字符串时,当您必须执行普遍的搜索操做。  StringBuilder   选件类没有搜索方法 ,如 IndexOf 或 StartsWith。  您必须转换为 String 的 StringBuilder 对象这些操做的,这样,能够对从使用 StringBuilder的性能。  有关详细信息,请参阅 搜索在 StringBuilder 对象的文本 部分。 

在这些条件下考虑使用 StringBuilder 选件类:

  • 当您但愿您的应用程序建立一个未知的设置为字符串的更改在设计时 (例如,当您使用循环链接包含用户输入的随机数字符串)。

  • 当您但愿您的应用程序建立一个大量为字符串的更改。

  StringBuilder    对象的默认值容量为 16 个字符。

个人理解

  虽然String是引用类型,可是MS处理的时候,好比“+=”的运算符处理String的时候,是从新申请一块托管堆,用来存储处理后的新String,这样新String的HashCode(地址)也会随着变化,毕竟是一个新的地址引用,因此表面上很像是个“值类型”。

  所以,MS为了处理Sring带来的潜在性能问题,就加入了StringBuilder,固然咱们这里自只用到了StringBuilder不多一部分功能(字符串相加),StringBuilder还有不少其余性能很好的功能(这里不表)。我的StringBuilder确定比String复杂,初始化应该是要慢一点,确实咱们在上面测试中的用例1,3告诉咱们,二者实例化差距仍是很大的。在处理多个字符串相加的时候,性能上仍是StringBuilder要强些个人理解是:StringBulider的对面默认值容量是16个字符(Framework4.5),因此当处理的字符串大于16的时候,就会从新申请开辟另外一块当前StringBuilder字符长度,(每当追加操做致使 StringBuilder 对象的长度超过其容量,其现有的容量翻倍),这是占必定的性能损耗的,而String每次都是申请一块托管堆,因此StringBuilder在多字符串处理的时候性能应该是大于等于String(不考虑内部算法逻辑,只是猜想)

  在不一样的Framework中,StringBuilder的性能有时候很快,有时候很慢,这是能够理解的,毕竟设计一个类是有使用场景的,而已StringBuilder也提供了修改默认容量的方法,只是咱们在使用的时候没有根据现实场景对这个进行设置。因此极小的字符串相加处理,就能够直接用String来用,可是若是很是频繁的处理,好比webApi被调用,那么是否是考虑用StringBuilder来,而且,经过测试来预估StringBuilder的默认容量范围,这样来达到性能的极致。

  在不一样的Framework中,2.0比1.0是有个革命性的变化,好比泛型等等。3.0和3.5加了不少功能,好比WPF,WCF,WFF,Linq等,但他们只是在2.0的基础上作了扩展,丰富,实际底层改变不大,你们从3.0和3.5的安装版就能够看出,他们都是100多M,都是经过打补丁的方式来弄的,因此安装包很大。到了4.0,Framework发生的大变化,我的以为最实质的变化就是引入了System.Core。而且把全部的都从新实现了一遍,因此安装版只有40多M,加了这么多东西,从28->40非常能够的。4.5没有研究,但从4.5引用的仍是4.0的System.Core,就应该是底层变化不大,若是你们有知道,了解的,但愿告诉下咱们。因此上面结果中,有时候4.0,4.5领先不少,在有的时候落后不少,也没什么的,估计只是策略变了而已。

  Framework也开源了,你们均可以去关注下,我也会抽时间去研究研究,看何时能在未来的Framework中看到本身提交的代码,那是多么愉悦的事情啊,多谢了github这个分布式版本管理,咱们就能够修改提交了,审核过不过无所谓,毕竟提交过,哈哈。若是有一天能审核过,那能够面试的时候或者跟猿们聊天的时候,多BB啊。

  一写就到了23:30了,明天还要上班,收笔把,还准备把反射出的不一样版本的String和StringBuilder的代码贴出来的,打住了,身体也很重要。

Demo下载

源码下载

本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文连接,谢谢合做。

文章是哥(mephisto)写的,SourceLink

相关文章
相关标签/搜索