[英]Why is StringBuilder slower than string concatenation?
与+串联相比,为什么StringBuilder
变慢? StringBuilder
旨在避免额外的对象创建,但为什么它会惩罚性能呢?
static void Main(string[] args)
{
int max = 1000000;
for (int times = 0; times < 5; times++)
{
Console.WriteLine("\ntime: {0}", (times+1).ToString());
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
sw.Stop();
Console.WriteLine("String +\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
sw = Stopwatch.StartNew();
for (int j = 0; j < max; j++)
{
StringBuilder msg = new StringBuilder();
msg.Append("Your total is ");
msg.Append("$500 ");
msg.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
Console.Read();
}
编辑:按建议移出范围变量:
更改以便不会一直实例化StringBuilder,而是.Clear()
它:
time: 1
String + : 3348ms
StringBuilder : 3151ms
time: 2
String + : 3346ms
StringBuilder : 3050ms
等等
请注意 ,这仍然测试完全相同的功能,但尝试更智能地重用资源。
代码:(也访问http://ideone.com/YuaqY )
using System;
using System.Text;
using System.Diagnostics;
public class Program
{
static void Main(string[] args)
{
int max = 1000000;
for (int times = 0; times < 5; times++)
{
{
Console.WriteLine("\ntime: {0}", (times+1).ToString());
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
sw.Stop();
Console.WriteLine("String +\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
{
Stopwatch sw = Stopwatch.StartNew();
StringBuilder msg = new StringBuilder();
for (int j = 0; j < max; j++)
{
msg.Clear();
msg.Append("Your total is ");
msg.Append("$500 ");
msg.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
}
Console.Read();
}
}
您正在每次迭代时创建一个StringBuilder
的新实例,这会产生一些开销。 既然你没有将它用于它实际意图做的事情(即:构建大字符串,否则需要许多字符串连接操作),看到比串联更糟糕的性能并不奇怪。
StringBuilder
更常见的比较/用法如下:
string msg = "";
for (int i = 0; i < max; i++)
{
msg += "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
StringBuilder msg_sb = new StringBuilder();
for (int j = 0; j < max; j++)
{
msg_sb.Append("Your total is ");
msg_sb.Append("$500 ");
msg_sb.Append(DateTime.Now);
}
有了这个,你会发现StringBuilder
和串联之间存在显着的性能差异。 而“重要”是指数量级 ,而不是您在示例中观察到的~10%的差异。
由于StringBuilder
不需要构建大量的中间字符串而不会被丢弃,因此可以获得更好的性能。 这就是它的意思。 对于较小的字符串,最好使用字符串连接以简化和清晰。
使用更长的字符串时,StringBuilder的好处应该是显而易见的。
每次连接字符串时都会创建一个新的字符串对象,因此字符串越长,从旧字符串复制到新字符串所需的越多。
此外,创建许多临时对象可能会对StopWatch无法测量的性能产生负面影响,因为它会使用临时对象“污染”托管堆,并可能导致更多垃圾回收周期。
修改你的测试以创建(更多)更长的字符串并使用(更多)更多的连接/追加操作,并且StringBuilder应该表现更好。
除了不以最有效的方式使用StringBuilder
之外,您还没有尽可能有效地使用字符串连接。 如果你知道你提前连接了多少个字符串,那么在一行上完成这一切应该是最快的。 编译器优化操作,以便不生成任何中间字符串。
我添加了几个测试用例。 一个与sehe建议的基本相同,另一个在一行中生成字符串:
sw = Stopwatch.StartNew();
builder = new StringBuilder();
for (int j = 0; j < max; j++)
{
builder.Clear();
builder.Append("Your total is ");
builder.Append("$500 ");
builder.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder (clearing)\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
msg = "Your total is " + "$500" + DateTime.Now;
}
sw.Stop();
Console.WriteLine("String + (one line)\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
以下是我在机器上看到的输出示例:
time: 1
String + : 3707ms
StringBuilder : 3910ms
StringBuilder (clearing) : 3683ms
String + (one line) : 3645ms
time: 2
String + : 3703ms
StringBuilder : 3926ms
StringBuilder (clearing) : 3666ms
String + (one line) : 3625ms
一般来说: - 如果你在很多步骤中构建一个大字符串,或者你不知道将多少个字符串连接在一起, StringBuilder
会做得更好。
- 只要它是一个合理的选项选项,将它们全部混合在一个表达式中就更好了。
注意
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
汇编到
string msg = String.Concat("Your total is ", "$500 ");
msg = String.Concat(msg, DateTime.Now.ToString());
每次迭代总计两个concats和一个ToString。 此外,单个String.Concat非常快,因为它知道结果字符串的大小,因此它只分配结果字符串一次,然后快速将源字符串复制到其中。 这意味着在实践中
String.Concat(x, y);
永远都会超越
StringBuilder builder = new StringBuilder();
builder.Append(x);
builder.Append(y);
因为StringBuilder不能使用这样的快捷方式(你可以调用一个附加的,或者一个删除,这是String.Concat无法实现的)。
StringBuilder的工作方式是分配一个初始缓冲区并将字符串长度设置为0.对于每个Append,它必须检查缓冲区,可能分配更多缓冲区空间(通常将旧缓冲区复制到新缓冲区),复制字符串并增加构建器的字符串长度。 String.Concat不需要做所有这些额外的工作。
因此,对于简单的字符串连接,x + y(即String.Concat)将始终优于StringBuilder。
现在,一旦开始将大量字符串连接到单个缓冲区中,或者你在缓冲区上进行大量操作,你就会开始从StringBuilder中获益,在不使用StringBuilder时你需要继续创建新的字符串。 。 这是因为StringBuilder只是偶尔以块的形式分配新的内存,但String.Concat,String.SubString等(几乎)总是分配新的内存。 (像“”.SubString(0,0)或String.Concat(“”,“”)之类的东西不会分配内存,但这些都是退化的情况。)
我认为比较String和StringBuilder之间的效率而不是时间更好。
msdn说:String被称为不可变的,因为它的值一旦创建就无法修改。 看似修改String的方法实际上返回一个包含修改的新String。 如果需要修改类似字符串的对象的实际内容,请使用System.Text.StringBuilder类。
string msg = "Your total is "; // a new string object
msg += "$500 "; // a new string object
msg += DateTime.Now; // a new string object
看哪一个更好。
下面是一个示例,演示了StringBuilder
执行速度比字符串连接更快的情况:
static void Main(string[] args)
{
const int sLen = 30, Loops = 10000;
DateTime sTime, eTime;
int i;
string sSource = new String('X', sLen);
string sDest = "";
//
// Time StringBuilder.
//
for (int times = 0; times < 5; times++)
{
sTime = DateTime.Now;
System.Text.StringBuilder sb = new System.Text.StringBuilder((int)(sLen * Loops * 1.1));
Console.WriteLine("Result # " + (times + 1).ToString());
for (i = 0; i < Loops; i++)
{
sb.Append(sSource);
}
sDest = sb.ToString();
eTime = DateTime.Now;
Console.WriteLine("String Builder took :" + (eTime - sTime).TotalSeconds + " seconds.");
//
// Time string concatenation.
//
sTime = DateTime.Now;
for (i = 0; i < Loops; i++)
{
sDest += sSource;
//Console.WriteLine(i);
}
eTime = DateTime.Now;
Console.WriteLine("Concatenation took : " + (eTime - sTime).TotalSeconds + " seconds.");
Console.WriteLine("\n");
}
//
// Make the console window stay open
// so that you can see the results when running from the IDE.
//
}
结果#1 String Builder占用:0秒。 连接采取:8.7659616秒。
结果#2 String Builder占用:0秒。 连接采取:8.7659616秒。
结果#3 String Builder占用:0秒。 连接采取:8.9378432秒。
结果#4 String Builder占用:0秒。 连接采取:8.7972128秒。
结果#5 String Builder占用:0秒。 连接采取:8.8753408秒。
StringBulder
比+串联快得多..
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.