Csharp/C#教程:浅谈C# StringBuilder内存碎片对性能的影响分享

StringBuilder内部是由多段char[]组成的半自动链表,因此频繁从中间修改StringBuilder,会将原本连续的内存分隔为多段,从而影响读取/遍历性能。

连续内存与不连续内存的性能差,可能高达1600倍。

背景

用StringBuilder的用户可能大都想用StringBuilder拼接html/json模板、组装动态SQL等正常操作。但在一些特殊场景中——如为某种编程语言写语言服务,或者写一个富文本编辑器时,StringBuilder依然也有用武之地,通过里面的Insert/Remove两个方法来修改。

测试方法

Talkischeap,showmethecode:

intdocLength=10000; voidMain() { (frompowerinEnumerable.Range(1,16) letmutations=(int)Math.Pow(2,power) selectnew { mutations, PerformanceRatio=Math.Round(GetPerformanceRatio(docLength,mutations),1) }).Dump(); } floatGetPerformanceRatio(intdocLength,intmutations) { varsb=newStringBuilder("".PadRight(docLength)); varbefore=GetPerformance(sb); FragmentStringBuilder(sb,mutations); varafter=GetPerformance(sb); return(float)after.Ticks/before.Ticks; } voidFragmentStringBuilder(StringBuildersb,intmutations) { varr=newRandom(42); for(inti=0;i<mutations;i++) { sb.Insert(r.Next(sb.Length),'x'); sb.Remove(r.Next(sb.Length),1); } } TimeSpanGetPerformance(StringBuildersb) { varsw=Stopwatch.StartNew(); longtot=0; for(inti=0;i<sb.Length;i++) { charc=sb[i]; tot+=(int)c; } sw.Stop(); returnsw.Elapsed; }

关于这段代码,请注意以下几点:

通过.PadRight(n)来直接创建长度为n的空白字符串,可以用newstring(”,n)来代替; newRandom(42)处,我指定了一个随机因子,确保每次分隔后分隔的位置完全相同,有利于做对照组; 我分别对字符串进行了2^1~2^16次修改,分别比较经过这么多次修改之后的性能差异; 我使用sb[i]来逐一访问StringBuilder中的位置,使内存不连续性更加突显。

运行结果

mutations PerformanceRatio 2 1 4 1 8 1 16 1 32 1 64 1.1 128 1.2 256 1.8 512 5.2 1024 19.9 2048 81.3 4096 274.5 8192 745.8 16384 1578.8 32768 1630.4 65536 930.8

可见如果在StringBuilder中间进行大量修改,其性能会急据下降,注意看32768次修改的情况下,遍历时会产生高达1630.4倍的性能差!

解决方式

如果一定要用StringBuilder,可以考虑在修改一定次数后,重新创建一个新的StringBuilder,以使得访问时获得最佳的内存连续性,即可解决此问题:

voidFragmentStringBuilder(StringBuildersb,intmutations) { varr=newRandom(42); for(inti=0;i<mutations;i++) { sb.Insert(r.Next(sb.Length),'x'); sb.Remove(r.Next(sb.Length),1); //重点 constintdefragmentCount=250; if(i%defragmentCount==defragmentCount-1) { stringbuf=sb.ToString(); sb.Clear(); sb.Append(buf); } } }

如上,每经过250次修改,即将原StringBuilder删除,然后重新创建一个新的StringBuilder,此时运行效果如下:

mutations PerformanceRatio 2 1.2 4 0.7 8 1 16 1 32 1 64 1.1 128 1.2 256 1 512 1 1024 1 2048 1 4096 1.1 8192 1.5 16384 1.3 32768 1 65536 1

可见,在几乎所有情况下,受内存不连续造成的访问性能问题,解决——同时250可能是一个相对比较合理的数字,在插入性能与查询/遍历性能中,获得平衡。

反思与上述就是C#学习教程:浅谈C# StringBuilder内存碎片对性能的影响分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/903941.html

(0)
上一篇 2021年10月21日
下一篇 2021年10月21日

精彩推荐