.net &引用;Don';t在此热代码路径中使用StringBuilder或foreach“;

.net &引用;Don';t在此热代码路径中使用StringBuilder或foreach“;,.net,optimization,foreach,signalr,stringbuilder,.net,Optimization,Foreach,Signalr,Stringbuilder,我正在浏览开源项目的源代码,我看到了标题为“不要在这个热门代码路径中使用StringBuilder或foreach”的代码: -公共静态字符串MakeCursor(IEnumerable游标) +公共静态字符串MakeCursor(IList游标) { -var sb=新的StringBuilder(); -bool first=true; -foreach(游标中的变量c) +var结果=”; +对于(int i=0;i0) { -某人附加(“|”); +结果+=“|”; } -某人附加(转

我正在浏览开源项目的源代码,我看到了标题为“不要在这个热门代码路径中使用StringBuilder或foreach”的代码:

-公共静态字符串MakeCursor(IEnumerable游标)
+公共静态字符串MakeCursor(IList游标)
{ 
-var sb=新的StringBuilder();
-bool first=true;
-foreach(游标中的变量c)
+var结果=”;
+对于(int i=0;i0)
{
-某人附加(“|”);
+结果+=“|”;
}
-某人附加(转义(c.Key));
-某人附加(“,”);
-某人附加(c.Id);
-第一个=假;
+结果+=转义(游标[i].Key);
+结果+=',';
+结果+=游标[i].Id;
}
-使某人返回字符串();
+返回结果;
}
我理解为什么foreach有时效率较低,为什么它被foreach取代

然而,我了解并体验到StringBuilder是连接字符串的最有效的方法。所以我想知道为什么作者决定用标准连接代替它


使用StringBuilder在这里和一般情况下有什么错误?

您正在进行多少次连接?如果数量众多,请使用StringBuilder。如果只是少数,那么创建StringBuilder的开销将超过任何优势。

希望改变这一点的人能够真正衡量出差异

  • 每次实例化一个新的stringbuilder都会有开销。 这也给内存/垃圾收集带来了压力
  • 编译器可以为简单的连接生成“stringbuilderlike”代码
  • FOR实际上可能较慢,因为它可能需要边界检查,而边界检查不是 完成foreach循环,因为编译器“知道”它们在范围内

    • 我会把钱放在

                 StringBuilder sb = new StringBuilder();
                 bool first = true;
                 foreach (Cursor c in cursors)
                 {
                      if (first)
                      {
                         first = false;  // only assign it once
                      }
                      else
                      {
                          sb.Append('|');
                      }
                      sb.Append(Escape(c.Key) + ',' + c.Id);
                  }
                  return sb.ToString();
      

      但是我会把我的钱和来自dfowler的更新放在一起。查看他的答案中的链接。

      这取决于提供给函数的
      光标的数量

      当包含4-10个字符串时,两种方法之间的大多数比较似乎更倾向于
      StringBuilder
      而不是字符串包含。如果我没有明确的理由,我很可能会选择
      StringBuilder
      (例如我的问题/应用程序的两种方法的性能比较)。我会考虑在<代码> StringBuilder <代码>中预先分配一个缓冲区来避免(多个)重置。
      有关此主题的一些讨论,请参阅和

      我对代码进行了更改,是的,它在分配(GetEnumerator())调用的数量上与非分配调用的数量上产生了巨大的差异。想象一下,这段代码是每秒数百万次。分配的枚举数的数量是荒谬的,可以避免

      编辑: 我们现在反转控制以避免任何分配(直接写入写入程序):

      是什么让你认为
      foreach
      的效率低于
      for
      ?这取决于实施情况。是的,在这里使用重复的字符串连接看起来是个糟糕的主意——特别是如果有很多游标的话。我非常同意Jon的观点。如果StringBuilder版本的性能比串联版本差,我会感到惊讶。根据循环迭代次数,如果使用更大的启动容量初始化StringBuilder,则可能有助于防止某些“隐藏”隐式对象初始化。你测量/分析了这两个版本以比较它们了吗?你应该要求它:)我做了代码更改,是的,它在分配(GetEnumerator())调用的数量上与非分配调用的数量上有很大的不同。想象一下,这段代码是每秒数百万次。分配的枚举数的数量是荒谬的,可以避免。查看新版本:后续注意,在这种情况下,我们只需要得到两个游标,就可以得到7个字符串连接,这是非常讨厌的。不一定-如果大小未知,您可能会得到内部缓冲区的多次重新分配。这可能会很昂贵。那么正确的方法不是看一下游标的数量,为很少的人做一个路径(字符串),为很多人做另一个路径(StringBuilder)?谢谢您提供的信息。这个相关的SO问题也非常令人费解:了解该项目并与David Fowler一起跟踪JabbR的进展,我几乎可以肯定这正是促使改变的原因。他花了大量时间使用CLRProfiler等工具进行测量,并处理所有正在创建的垃圾。希望他能详细介绍这条线索。大卫,不使用foreach,但仍然使用StringBuilder怎么样?非常感谢你花时间构建一个原型,向我们展示这些更改的优点。如果我理解正确,StringBuilder更适合处理长字符串,而不是在长循环中处理短字符串。您不应该链接到master,而应该链接到项目历史记录中的特定提交-_-“如果对性能感兴趣,我认为正确的方法应该是读取
      .Count
      一次,如果它等于一个返回
      游标[0]。Key+,'+游标[0]。id
      ;如果是两个,则返回
      游标[0]。键+,'+游标[0]。id+'|'+游标[1]。键+','+游标[1]。id
      (注意:此处没有循环,因此只有一个字符串分配)。如果超过两个,我可能会使用
      StringBuilder
      ,可能会在每个项目之后放置
      |
      ,并在最后的
      ToString()之前减少末尾的长度;最佳性能可能是分配一组
      
                 StringBuilder sb = new StringBuilder();
                 bool first = true;
                 foreach (Cursor c in cursors)
                 {
                      if (first)
                      {
                         first = false;  // only assign it once
                      }
                      else
                      {
                          sb.Append('|');
                      }
                      sb.Append(Escape(c.Key) + ',' + c.Id);
                  }
                  return sb.ToString();