简体   繁体   English

为什么StringBuilder比字符串连接慢?

[英]Why is StringBuilder slower than string concatenation?

Why is StringBuilder slower when compared to + concatenation? 与+串联相比,为什么StringBuilder变慢? StringBuilder was meant to avoid extra object creation, but why does it penalize performance? 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();
    }

在此输入图像描述

EDIT: Moving out of scope variables as suggested: 编辑:按建议移出范围变量:

在此输入图像描述

Change so that the StringBuilder isn't instantiated all the time, instead .Clear() it: 更改以便不会一直实例化StringBuilder,而是.Clear()它:

time: 1
String +    :   3348ms
StringBuilder   :   3151ms

time: 2
String +    :   3346ms
StringBuilder   :   3050ms

etc. 等等

Note that this still tests exactly the same functionality, but tries to reuse resources a bit smarter. 请注意 ,这仍然测试完全相同的功能,但尝试更智能地重用资源。

Code: (also live on http://ideone.com/YuaqY ) 代码:(也访问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();
    }
}

You are creating a new instance of StringBuilder with every iteration, and that incurs some overhead. 您正在每次迭代时创建一个StringBuilder的新实例,这会产生一些开销。 Since you are not using it for what it's actually meant to do (ie: build large strings which would otherwise require many string concatenation operations), it's not surprising to see worse performance than concatenation. 既然你没有将它用于它实际意图做的事情(即:构建大字符串,否则需要许多字符串连接操作),看到比串联更糟糕的性能并不奇怪。

A more common comparison / usage of StringBuilder is something like: 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);
}

With this, you'll observe a significant performance difference between StringBuilder and concatenation. 有了这个,你会发现StringBuilder和串联之间存在显着的性能差异。 And by "significant" I mean orders of magnitude , not the ~ 10% difference you are observing in your examples. 而“重要”是指数量级 ,而不是您在示例中观察到的~10%的差异。

Since StringBuilder doesn't have to build tons of intermediary strings that will just get thrown away, you get much better performance. 由于StringBuilder不需要构建大量的中间字符串而不会被丢弃,因此可以获得更好的性能。 That's what it's meant for. 这就是它的意思。 For smaller strings, you are better off using string concatenation for simplicity and clarity. 对于较小的字符串,最好使用字符串连接以简化和清晰。

The benefits of StringBuilder should be noticeable with longer strings. 使用更长的字符串时,StringBuilder的好处应该是显而易见的。

Every time you concatenate a string you create a new string object, so the longer the string, the more is needed to copy from the old string to the new string. 每次连接字符串时都会创建一个新的字符串对象,因此字符串越长,从旧字符串复制到新字符串所需的越多。

Also, creating many temporary objects may have an adverse effect on performance that is not measurable by a StopWatch, because it "pollutes" the managed heap with temporary objects and may cause more garbage collection cycles. 此外,创建许多临时对象可能会对StopWatch无法测量的性能产生负面影响,因为它会使用临时对象“污染”托管堆,并可能导致更多垃圾回收周期。

Modify your test to create (much) longer strings and use (many) more concatenations / append operations and the StringBuilder should perform better. 修改你的测试以创建(更多)更长的字符串并使用(更多)更多的连接/追加操作,并且StringBuilder应该表现更好。

In addition to not using StringBuilder as in the most efficient manner, you're also not using string concatenation as efficiently as possible. 除了不以最有效的方式使用StringBuilder之外,您还没有尽可能有效地使用字符串连接。 If you know how many strings you're concatenating ahead of time, then doing it all on one line should be fastest. 如果你知道你提前连接了多少个字符串,那么在一行上完成这一切应该是最快的。 The compiler optimizes the operation so that no intermediate strings are generated. 编译器优化操作,以便不生成任何中间字符串。

I added a couple more test cases. 我添加了几个测试用例。 One is basically the same as what sehe suggested, and the other generates the string in one line: 一个与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));

And here is an example of the output I see on my machine: 以下是我在机器上看到的输出示例:

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

In general: - StringBuilder does better if you're building a large string in a lot of steps, or you don't know how many strings will be concatenated together. 一般来说: - 如果你在很多步骤中构建一个大字符串,或者你不知道将多少个字符串连接在一起, StringBuilder会做得更好。
- Mashing them all together in a single expression is better whenever it's a reasonable option option. - 只要它是一个合理的选项选项,将它们全部混合在一个表达式中就更好了。

Note that 注意

string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;

compiles down to 汇编到

string msg = String.Concat("Your total is ", "$500 ");
msg = String.Concat(msg, DateTime.Now.ToString());

This totals two concats and one ToString per iteration. 每次迭代总计两个concats和一个ToString。 Also, a single String.Concat is really fast, because it knows how large the resulting string will be, so it only allocates the resulting string once, and then quickly copies the source strings into it. 此外,单个String.Concat非常快,因为它知道结果字符串的大小,因此它只分配结果字符串一次,然后快速将源字符串复制到其中。 This means that in practice 这意味着在实践中

String.Concat(x, y);

will always outperform 永远都会超越

StringBuilder builder = new StringBuilder();
builder.Append(x);
builder.Append(y);

because StringBuilder cannot take such shortcuts (you could call a thirs Append, or a Remove, that's not possible with String.Concat). 因为StringBuilder不能使用这样的快捷方式(你可以调用一个附加的,或者一个删除,这是String.Concat无法实现的)。

The way a StringBuilder works is by allocating an initial buffer and set the string length to 0. With each Append, it has to check the buffer, possibly allocate more buffer space (usually copying the old buffer to the new buffer), copy the string and increment the string length of the builder. StringBuilder的工作方式是分配一个初始缓冲区并将字符串长度设置为0.对于每个Append,它必须检查缓冲区,可能分配更多缓冲区空间(通常将旧缓冲区复制到新缓冲区),复制字符串并增加构建器的字符串长度。 String.Concat does not need to do all this extra work. String.Concat不需要做所有这些额外的工作。

So for simple string concatenations, x + y (ie, String.Concat) will always outperform StringBuilder. 因此,对于简单的字符串连接,x + y(即String.Concat)将始终优于StringBuilder。

Now, you'll start to get benefits from StringBuilder once you start concatenating lots of strings into a single buffer, or you're doing lots of manipulations on the buffer, where you'd need to keep creating new strings when not using a StringBuilder. 现在,一旦开始将大量字符串连接到单个缓冲区中,或者你在缓冲区上进行大量操作,你就会开始从StringBuilder中获益,在不使用StringBuilder时你需要继续创建新的字符串。 。 This is because StringBuilder only occasionally allocates new memory, in chunks, but String.Concat, String.SubString, etc. (nearly) always allocate new memory. 这是因为StringBuilder只是偶尔以块的形式分配新的内存,但String.Concat,String.SubString等(几乎)总是分配新的内存。 (Something like "".SubString(0,0) or String.Concat("", "") won't allocate memory, but those are degenerate cases.) (像“”.SubString(0,0)或String.Concat(“”,“”)之类的东西不会分配内存,但这些都是退化的情况。)

I think its better to compare effeciancy between String and StringBuilder rather then time. 我认为比较String和StringBuilder之间的效率而不是时间更好。

what msdn says: A String is called immutable because its value cannot be modified once it has been created. msdn说:String被称为不可变的,因为它的值一旦创建就无法修改。 Methods that appear to modify a String actually return a new String containing the modification. 看似修改String的方法实际上返回一个包含修改的新String。 If it is necessary to modify the actual contents of a string-like object, use the System.Text.StringBuilder class. 如果需要修改类似字符串的对象的实际内容,请使用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

see which one is better. 看哪一个更好。

Here is an example that demonstrates a situation in which StringBuilder will execute more quickly than string concatenation: 下面是一个示例,演示了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.
    // 
}

Result # 1 String Builder took :0 seconds. 结果#1 String Builder占用:0秒。 Concatenation took : 8.7659616 seconds. 连接采取:8.7659616秒。

Result # 2 String Builder took :0 seconds. 结果#2 String Builder占用:0秒。 Concatenation took : 8.7659616 seconds. 连接采取:8.7659616秒。

Result # 3 String Builder took :0 seconds. 结果#3 String Builder占用:0秒。 Concatenation took : 8.9378432 seconds. 连接采取:8.9378432秒。

Result # 4 String Builder took :0 seconds. 结果#4 String Builder占用:0秒。 Concatenation took : 8.7972128 seconds. 连接采取:8.7972128秒。

Result # 5 String Builder took :0 seconds. 结果#5 String Builder占用:0秒。 Concatenation took : 8.8753408 seconds. 连接采取:8.8753408秒。

StringBulder is much faster than + concatenation.. StringBulder比+串联快得多..

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM