Mat*_*ley 7 c# string optimization performance
以下C#代码需要5分钟才能运行:
int i = 1;
string fraction = "";
while (fraction.Length < 1000000)
{
fraction += i.ToString();
i++;
}
Run Code Online (Sandbox Code Playgroud)
像这样"优化它"使它在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;
}
Run Code Online (Sandbox Code Playgroud)
编辑:有人建议使用StringBuilder,这也是一个很好的建议,这出现在0.06s:
int i = 1;
StringBuilder fraction = new StringBuilder();
while (fraction.Length < 1000000)
{
fraction.Append(i);
i++;
}
Run Code Online (Sandbox Code Playgroud)
四处寻找最佳值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++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下:
Concat: 5879ms
StringBuilder as in post: 206ms
StringBuilder calling Append(i): 196ms
StringBuilder with appropriate capacity: 184ms
Run Code Online (Sandbox Code Playgroud)
你的串联比第一个解决方案更快的原因很简单 - 你正在做几个"廉价"的连接(每次复制相对较少的数据)和相对较少的"大"连接(到目前为止整个字符串) .在原文中,每一步都会复制到目前为止获得的所有数据,这显然更加昂贵.
使用StringBuilder连接超过(大约)5个字符串(结果可能略有不同).另外,为StringBuilder的构造函数提供预期最大大小的提示.
[更新]:只评论您对该问题的修改.StringBuilder如果你对连接字符串的最终大小有一个近似(或确切)的想法,你也可以提高性能,因为这会减少它必须执行的内存分配数量:
// e.g. Initialise to 10MB
StringBuilder fraction = new StringBuilder(10000000);
Run Code Online (Sandbox Code Playgroud)
您可能会看到前1000个字符几乎没有时间反对最后1000个字符.
我认为耗时的部分是每次添加一个对你的计算机来说很难的字符时,将大字符串实际复制到一个新的内存区域.
您可以轻松地将优化与通常使用流进行比较,使用缓冲区.较大的块通常会导致更好的性能,直到您达到不再有任何差异的临界大小,并且在处理少量数据时开始变得不利.
但是,如果您从一开始就定义了一个具有适当大小的char数组,那么它可能会非常快速,因为它不会一遍又一遍地复制它.