建设购物网站的条件,北海哪家公司做网站建设研发,wordpress 页面模板制作,重庆招聘网站哪个好TL;DR:StringBuilder内部是由多段 char[]组成的半自动链表#xff0c;因此频繁从中间修改 StringBuilder#xff0c;会将原本连续的内存分隔为多段#xff0c;从而影响读取/遍历性能。连续内存与不连续内存的性能差#xff0c;可能高达 1600倍。背景用 StringBuilder的用户… TL;DR:StringBuilder内部是由多段 char[]组成的半自动链表因此频繁从中间修改 StringBuilder会将原本连续的内存分隔为多段从而影响读取/遍历性能。连续内存与不连续内存的性能差可能高达 1600倍。背景用 StringBuilder的用户可能大都想用 StringBuilder拼接 html/json模板、组装动态 SQL等正常操作。但在一些特殊场景中——如为某种编程语言写语言服务或者写一个富文本编辑器时 StringBuilder依然也有用武之地通过里面的 Insert/ Remove两个方法来修改。测试方法Talk is cheap, show me the code:int docLength 10000;
void Main()
{(from power in Enumerable.Range (1, 16)let mutations (int) Math.Pow (2, power)select new{mutations,PerformanceRatio Math.Round (GetPerformanceRatio (docLength, mutations), 1)}).Dump();
}
float GetPerformanceRatio (int docLength, int mutations)
{var sb new StringBuilder (.PadRight (docLength));var before GetPerformance (sb);FragmentStringBuilder (sb, mutations);var after GetPerformance (sb);return (float) after.Ticks / before.Ticks;
}
void FragmentStringBuilder (StringBuilder sb, int mutations)
{var r new Random(42);for (int i 0; i mutations; i){sb.Insert (r.Next (sb.Length), x);sb.Remove (r.Next (sb.Length), 1);}
}
TimeSpan GetPerformance (StringBuilder sb)
{var sw Stopwatch.StartNew();long tot 0;for (int i 0; i sb.Length; i){char c sb[i];tot (int) c;}sw.Stop();return sw.Elapsed;
}
关于这段代码请注意以下几点通过 .PadRight(n)来直接创建长度为 n的空白字符串可以用 newstring( ,n)来代替newRandom(42)处我指定了一个随机因子42确保每次分隔后分隔的位置完全相同有利于做对照组我分别对字符串进行了 2^1~2^16次修改分别比较经过这么多次修改之后的性能差异我使用 sb[i]来逐一访问 StringBuilder中的位置使内存不连续性更加突显。运行结果mutationsPerformanceRatio214181161321641.11281.22561.85125.2102419.9204881.34096274.58192745.8163841578.8327681630.465536930.8可见如果在 StringBuilder中间进行大量修改其性能会急剧下降注意看 32768次修改的情况下遍历时会产生高达 1630.4倍的性能差解决方式如果一定要用 StringBuilder可以考虑在修改一定次数后重新创建一个新的 StringBuilder以使得访问时获得最佳的内存连续性即可解决此问题void FragmentStringBuilder (StringBuilder sb, int mutations)
{var r new Random(42);for (int i 0; i mutations; i){sb.Insert (r.Next (sb.Length), x);sb.Remove (r.Next (sb.Length), 1);// 重点const int defragmentCount 250;if (i % defragmentCount defragmentCount - 1){string buf sb.ToString();sb.Clear();sb.Append(buf);}}
}
如上每经过 250次修改即将原 StringBuilder删除然后重新创建一个新的 StringBuilder此时运行效果如下mutationsPerformanceRatio21.240.781161321641.11281.225615121102412048140961.181921.5163841.3327681655361可见在几乎所有情况下受内存不连续造成的访问性能问题解决——同时 250可能是一个相对比较合理的数字在插入性能与查询/遍历性能中获得平衡。反思与总结众所周知由于 string的不可变性拼接大量字符串时会浪费大量内存。但使用 StringBuilder也需要了解它的结构。StringBuilder这样做成链式的结构并非没有原因如果考虑插入性能做成链式接口是最优秀的。但如果考虑查询性能链式结构就非常不利了如果设计为非链式结构从中间插入时 StringBuilder的内存空间可能不够因此需要重新分配内存这样相当于将 StringBuilder降格为 string因此完全丧失了 StringBuilder适合做“频繁插入”的优势。本文说的其实是一个非常特殊的例子现实中除了语言服务、编辑器外很少会需要这种即要频繁插入快也要频繁修改快的场景。如果想简单点搞用 StringBuilder会是一个有条件合适的解决方案。更适合的解决方案当然是专门的数据结构—— PieceTable微软在 VSCode编辑器中为了确保大文件编辑性能使用了该数据结构取得了非常不错的成果参考链接Text Buffer Reimplementation - https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation。喜欢的朋友请关注我的微信公众号【DotNet骚操作】