以下C#代码需要5分钟才能运行:
int i = 1; string fraction = ""; while (fraction.Length < 1000000) { fraction += i.ToString(); i++; }
像这样"优化它"使它在1.5秒内运行:
int i = 1; string fraction = ""; while (fraction.Length < 1000000) { // concatenating strings is much faster for small strings string tmp = ""; for (int j = 0; j < 1000; j++) { tmp += i.ToString(); i++; } fraction += tmp; }
编辑:有人建议使用StringBuilder
,这也是一个很好的建议,这出现在0.06s:
int i = 1; StringBuilder fraction = new StringBuilder(); while (fraction.Length < 1000000) { fraction.Append(i); i++; }
四处寻找最佳值j
是另一个时间的主题,但为什么这个非显而易见的优化工作如此之好呢?另外,在一个相关的主题上,我听说它应该永远不要使用+
带字符串的运算符,赞成string.Format()
,这是真的吗?
我根本没有得到你的结果.在我的盒子上,StringBuilder赢得了胜利.你能发布完整的测试程序吗?这是我的,有三个变体 - 你的字符串连接优化,"简单"的StringBuilder,以及具有初始容量的StringBuilder.我已经增加了限制因为我的盒子上的速度太快而无法进行有效测量.
using System; using System.Diagnostics; using System.Text; public class Test { const int Limit = 4000000; static void Main() { Time(Concatenation, "Concat"); Time(SimpleStringBuilder, "StringBuilder as in post"); Time(SimpleStringBuilderNoToString, "StringBuilder calling Append(i)"); Time(CapacityStringBuilder, "StringBuilder with appropriate capacity"); } static void Time(Action action, string name) { Stopwatch sw = Stopwatch.StartNew(); action(); sw.Stop(); Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds); GC.Collect(); GC.WaitForPendingFinalizers(); } static void Concatenation() { int i = 1; string fraction = ""; while (fraction.Length < Limit) { // concatenating strings is much faster for small strings string tmp = ""; for (int j = 0; j < 1000; j++) { tmp += i.ToString(); i++; } fraction += tmp; } } static void SimpleStringBuilder() { int i = 1; StringBuilder fraction = new StringBuilder(); while (fraction.Length < Limit) { fraction.Append(i.ToString()); i++; } } static void SimpleStringBuilderNoToString() { int i = 1; StringBuilder fraction = new StringBuilder(); while (fraction.Length < Limit) { fraction.Append(i); i++; } } static void CapacityStringBuilder() { int i = 1; StringBuilder fraction = new StringBuilder(Limit + 10); while (fraction.Length < Limit) { fraction.Append(i); i++; } } }
结果如下:
Concat: 5879ms StringBuilder as in post: 206ms StringBuilder calling Append(i): 196ms StringBuilder with appropriate capacity: 184ms
你的串联比第一个解决方案更快的原因很简单 - 你正在做几个"廉价"的连接(每次复制相对较少的数据)和相对较少的"大"连接(到目前为止整个字符串) .在原文中,每一步都会复制到目前为止获得的所有数据,这显然更加昂贵.
使用StringBuilder连接超过(大约)5个字符串(结果可能略有不同).另外,为StringBuilder的构造函数提供预期最大大小的提示.
[更新]:只评论您对该问题的修改.StringBuilder
如果你对连接字符串的最终大小有一个近似(或确切)的想法,你也可以提高性能,因为这会减少它必须执行的内存分配数量:
// e.g. Initialise to 10MB StringBuilder fraction = new StringBuilder(10000000);
您可能会看到前1000个字符几乎没有时间反对最后1000个字符.
我认为耗时的部分是每次添加一个对你的计算机来说很难的字符时,将大字符串实际复制到一个新的内存区域.
您可以轻松地将优化与通常使用流进行比较,使用缓冲区.较大的块通常会导致更好的性能,直到您达到不再有任何差异的临界大小,并且在处理少量数据时开始变得不利.
但是,如果您从一开始就定义了一个具有适当大小的char数组,那么它可能会非常快速,因为它不会一遍又一遍地复制它.