【问题标题】:How Much Faster is StringBuilder respect of concat operator + in C# .Net [duplicate]StringBuilder 在 C# .Net 中对 concat 运算符 + 的尊重要快多少 [重复]
【发布时间】:2014-03-20 14:42:12
【问题描述】:

StringBuilder 在 C# .Net 中对 concat 运算符 + 的尊重要快多少 它如何在低级别工作比 concat 更快?

【问题讨论】:

  • 您可以自己测量并找出答案。
  • 这取决于你正在做多少串联。同意@JohnSaunders。在您的环境中对其进行测试并自行测量。 (然后在此处发布您的结果)。
  • StringBuilder 更快,部分原因是它使用缓冲区而不是从堆中为每个操作分配一个新字符串。
  • 请记住,如果没有StringBuilder,您将创建新字符串,这意味着新对象,在某些时候会被垃圾回收。还应测量该延迟以进行有效比较。
  • C# StringBuilder Performance 让您对性能有一个很好的了解。

标签: c# .net


【解决方案1】:

微软开发者网络有一个很好的比较:

虽然 StringBuilder 和 String 都表示 字符,它们的实现方式不同。字符串是不可变的 类型。也就是说,每一个看似修改一个String对象的操作 实际上创建了一个新字符串。

例如下面C#中对String.Concat方法的调用 示例似乎更改了名为 value 的字符串变量的值。 实际上,Concat 方法返回的值对象具有不同的 来自传递给方法的值对象的值和地址。 请注意,该示例必须使用 /unsafe 编译器进行编译 选项。对于执行大量字符串操作的例程(例如 作为在循环中多次修改字符串的应用程序),修改 重复字符串会导致显着的性能损失。这 另一种方法是使用 StringBuilder,它是一个可变字符串类。 可变性意味着一旦创建了类的实例, 它可以通过追加、删除、替换或插入来修改 人物。 StringBuilder 对象维护一个缓冲区以容纳 对字符串的扩展。如果有空间,则将新数据附加到缓冲区 可用;否则,分配一个新的更大的缓冲区,数据来自 原缓冲区复制到新缓冲区,新数据为 然后附加到新缓冲区。

String 的推荐用法是:

  • 当您的应用程序将对字符串进行的更改数量非常少时。在这些情况下,StringBuilder 可能比String 提供可忽略不计的性能改进或没有性能改进。 (由于StringBuilder 处理其缓冲区的方式)

  • 当您执行固定数量的连接操作时,尤其是使用 String 文字时。编译器应将这些组合成一个操作。

  • 当您在构建 String 时必须执行大量搜索操作时。 StringBuilder 缺少 IndexOfStartsWith 等搜索方法。您必须将您的 StringBuilder 对象转换为 String 才能执行这些可能会影响性能的操作。

StringBuilder 的推荐用法:

  • 当您希望应用程序在设计时对字符串进行未知数量的更改时(例如循环/随机数字符串)。

  • 当您希望您的应用程序对 string 进行重大更改时。

所以问题真的不应该是“哪个表现更好”,而应该是“在哪种情况下比另一种更理想?”

【讨论】:

    【解决方案2】:

    只是为了对比另一个答案,这里有一个示例,显示在我的计算机上连接速度提高了约 20%。

    public static void ConcatTest()
    {
        int NumConcatenations = 10000000;
    
        const string Postfix = "postfix_";
    
        /** + Operator concatenation **/
        var sw = Stopwatch.StartNew();
        for (int x = 0; x < NumConcatenations; x++)
        {
            var concatResult = string.Empty;
    
            for (int i = 0; i < 2; i++)
            {
                concatResult += Postfix;
            }
        }
        var plusOperatorTime = sw.ElapsedMilliseconds;
    
        Console.WriteLine();
    
        sw.Reset();
    
        /** StringBuilder concatenation **/
        sw.Start();
        for (int x = 0; x < NumConcatenations; x++)
        {
            var builder = new StringBuilder(string.Empty);
            for (int i = 0; i < 2; i++)
            {
                builder.Append(Postfix);
            }
        }
        var stringBuilderTime = sw.ElapsedMilliseconds;
    
        Console.WriteLine(
            "Concatenation with + operator took {0} ms, stringbuilder took {1} ms",
            plusOperatorTime,
            stringBuilderTime);
    
        Console.ReadLine();
    }
    

    您是将小说中的单词连接成一本书,还是将一百万对名字姓氏连接成全名?您的使用决定了什么更快。测试您的具体实现是确定的唯一方法。

    【讨论】:

      【解决方案3】:

      哇...我正在测量 100 倍的性能差异。 Stringbuilder 快得多。随时告诉我我的代码有什么问题。

      在我的系统上:+ 为 200 毫秒,字符串生成器为 2 毫秒。

             static void Main(string[] args)
          {
              int NumConcatenations = 10000;
      
              const string Postfix = "postfix_";
              var concatResult = string.Empty;
      
              /** + Operator concatenation **/
              var sw = Stopwatch.StartNew();
              for (int i = 0; i < NumConcatenations; i++)
              {
                  concatResult += Postfix;
              }
              var plusOperatorTime = sw.ElapsedMilliseconds;
      
              Console.WriteLine();
      
              sw.Reset();
      
              /** StringBuilder concatenation **/
              var builder = new StringBuilder(string.Empty);
              sw.Start();
              for (int i = 0; i < NumConcatenations; i++)
              {
                  builder.Append(Postfix);
              }
              var stringBuilderTime = sw.ElapsedMilliseconds;
      
              Debug.Assert(concatResult.Length == builder.ToString().Length);
      
              Console.WriteLine(
                  "Concatenation with + operator took {0} ms, stringbuilder took {1} ms",
                  plusOperatorTime,
                  stringBuilderTime);
      
              Console.ReadLine();
          }
      

      【讨论】:

      • 公平地说,StringBuilder 类会在每次越界时预先分配内存并将内存中的缓冲区加倍(如果我没记错的话)。
      • 你不能只做一个比另一个更好的全局声明。它们各自适用于不同的环境,并且在适当使用时会胜过对方。如果一种方式比普遍更好,就没有必要有两种方式。
      • @AdamSears 不,它没有(不再)。那是 .NET 4.0 之前的实现。
      • @Servy:也许我误读了 StringBuilder 的reference source?我认为这就是它正在做的事情。可能不会加倍缓冲区,但肯定是提前预分配空间。
      • @AdamSears 它基本上是创建一个字符数组的链表,每个字符数组都是从所有附加的字符串中提取的。然后,当您调用 ToString 时,它会将它们全部展平为单个字符数组,而不是预先分配空间并在每个附加项上复制项目。
      猜你喜欢
      • 2011-01-05
      • 2012-02-04
      • 2014-10-18
      • 2015-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-27
      • 1970-01-01
      相关资源
      最近更新 更多