首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >StringBuilder.Append诉StringBuilder.AppendFormat

StringBuilder.Append诉StringBuilder.AppendFormat
EN

Stack Overflow用户
提问于 2009-04-02 16:29:02
回答 9查看 55.3K关注 0票数 41

我想知道关于StringBuilder,我有一个问题,我希望社区能够解释。

让我们忘记代码的可读性,其中哪个是更快的,为什么呢?

StringBuilder.Append

代码语言:javascript
复制
StringBuilder sb = new StringBuilder();
sb.Append(string1);
sb.Append("----");
sb.Append(string2);

StringBuilder.AppendFormat

代码语言:javascript
复制
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}----{1}",string1,string2);
EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2009-04-02 16:32:58

不知道string1string2的大小是不可能说出来的。

通过调用AppendFormat,它将预先分配缓冲区一次,给定格式字符串的长度和要插入的字符串,然后连接所有内容并将其插入缓冲区。对于非常大的字符串,这将比单独调用Append更有利,这可能导致缓冲区多次展开。

但是,对Append的三次调用可能触发缓冲区的增长,也可能不会触发缓冲区的增长,每次调用都执行检查。如果字符串足够小,并且没有触发缓冲区扩展,那么它将比调用AppendFormat更快,因为它不需要解析格式字符串来确定在哪里进行替换。

需要更多的数据才能得到明确的答案

应该注意的是,很少讨论使用静态班级 (乔恩的回答使用AppendWithCapacity提醒我这一点)。他的测试结果表明,这是最好的情况(假设您不必利用特定的格式说明符)。String.Concat也会做同样的事情,因为它将预先确定字符串的长度来连接和预分配缓冲区(由于通过参数循环构造而增加了一些开销)。它的性能将与Jon的AppendWithCapacity方法相媲美。

或者,只是普通的加法运算符,因为它编译为对String.Concat的调用,但需要注意的是,所有添加都在同一个表达式中:

代码语言:javascript
复制
// One call to String.Concat.
string result = a + b + c;

而非

代码语言:javascript
复制
// Two calls to String.Concat.
string result = a + b;
result = result + c;

为所有提供测试代码的人提供

您需要在单独的运行中运行您的测试用例(或者至少在单独测试运行的度量之间执行GC )。这样做的原因是,如果您确实运行了1,000,000次,为一次测试在循环的每一次迭代中创建一个新的StringBuilder,然后运行相同次数的下一次测试,创建另外的1,000,000个StringBuilder实例,那么GC很可能会在第二次测试期间介入并阻碍其计时。

票数 46
EN

Stack Overflow用户

发布于 2009-04-02 16:46:05

casperOne是正确的。一旦达到某个阈值,Append()方法就会比AppendFormat()慢。以下是每种方法100,000次迭代的不同长度和运行时间:

长度:1

代码语言:javascript
复制
Append()       - 50900
AppendFormat() - 126826

长度: 1000

代码语言:javascript
复制
Append()       - 1241938
AppendFormat() - 1337396

长度: 10,000

代码语言:javascript
复制
Append()       - 12482051
AppendFormat() - 12740862

长度:20 000

代码语言:javascript
复制
Append()       - 61029875
AppendFormat() - 60483914

当引入长度接近20,000的字符串时,AppendFormat()函数的性能将略优于Append()

这一切为什么要发生?见casperOne的回答

编辑:

我在发布配置下分别重新运行每个测试,并更新结果。

票数 26
EN

Stack Overflow用户

发布于 2009-04-02 17:05:19

casperOne完全准确,它依赖于数据。但是,假设您将这个类库编写为供第三方使用的类库--您将使用哪一个类库?

一种选择是充分利用这两个方面的优势--计算出实际需要追加多少数据,然后使用StringBuilder.EnsureCapacity来确保我们只需要调整一个缓冲区大小。

不过,如果我不太烦恼的话,我会使用Append x3 --因为解析每个调用上的字符串格式标记显然是有效的--它看起来“更有可能”更快。

请注意,我已经要求BCL团队提供一种“缓存格式化程序”,我们可以使用格式字符串创建这种格式化程序,然后重复使用。框架每次使用时都必须解析格式字符串,这真是太疯狂了。

编辑:好的,为了灵活起见,我编辑了John的代码,并添加了一个"AppendWithCapacity“,它只是首先计算出了必要的容量。以下是不同长度的结果--对于长度1,我使用了1,000,000次迭代;对于所有其他长度,我使用了100,000次。(这只是为了获得合理的运行时间。)所有时间都在米利斯。

不幸的是,表实际上并不能在这种情况下工作。长度为1,1000,10000,20000。

时代:

  • 附件:1624757997,17970
  • AppendFormat: 3924998541,18993
  • AppendWithCapacity: 139,189,1558,3085

所以当事情发生的时候,我从未见过AppendFormat击败了我--但我确实看到AppendWithCapacity以非常大的优势赢了。

下面是完整的代码:

代码语言:javascript
复制
using System;
using System.Diagnostics;
using System.Text;

public class StringBuilderTest
{            
    static void Append(string string1, string string2)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(string1);
        sb.Append("----");
        sb.Append(string2);
    }

    static void AppendWithCapacity(string string1, string string2)
    {
        int capacity = string1.Length + string2.Length + 4;
        StringBuilder sb = new StringBuilder(capacity);
        sb.Append(string1);
        sb.Append("----");
        sb.Append(string2);
    }

    static void AppendFormat(string string1, string string2)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendFormat("{0}----{1}", string1, string2);
    }

    static void Main(string[] args)
    {
        int size = int.Parse(args[0]);
        int iterations = int.Parse(args[1]);
        string method = args[2];

        Action<string,string> action;
        switch (method)
        {
            case "Append": action = Append; break;
            case "AppendWithCapacity": action = AppendWithCapacity; break;
            case "AppendFormat": action = AppendFormat; break;
            default: throw new ArgumentException();
        }

        string string1 = new string('x', size);
        string string2 = new string('y', size);

        // Make sure it's JITted
        action(string1, string2);
        GC.Collect();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < iterations; i++)
        {
            action(string1, string2);
        }
        sw.Stop();
        Console.WriteLine("Time: {0}ms", (int) sw.ElapsedMilliseconds);
    }
}
票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/710504

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档