32

我正在浏览 Open Source SignalR项目的源代码,我看到这个差异代码,标题为“不要在此热代码路径中使用 StringBuilder 或 foreach”

-           public static string MakeCursor(IEnumerable<Cursor> cursors)
+           public static string MakeCursor(IList<Cursor> cursors)
            { 
-               var sb = new StringBuilder();
-               bool first = true;
-               foreach (var c in cursors)
+               var result = "";
+               for (int i = 0; i < cursors.Count; i++)
                {
-                   if (!first)
+                   if (i > 0)
                    {
-                       sb.Append('|');
+                       result += '|';
                    }
-                   sb.Append(Escape(c.Key));
-                   sb.Append(',');
-                   sb.Append(c.Id);
-                   first = false;
+                   result += Escape(cursors[i].Key);
+                   result += ',';
+                   result += cursors[i].Id;
                }
-               return sb.ToString();
+               return result;
            }

我理解为什么 foreach 有时效率会降低,以及为什么它被 for 取代。

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

这里和一般使用 StringBuilder 有什么问题?

4

5 回答 5

30

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

编辑:我们现在反转控制以避免任何分配(直接写给作者): https ://github.com/SignalR/SignalR/blob/2.0.2/src/Microsoft.AspNet.SignalR.Core/Messaging/光标.cs#L36

于 2012-09-11T21:37:56.333 回答
3

只是希望改变这个的人实际测量了差异。

  • 每次实例化一个新的字符串生成器都会产生开销。这也给内存/垃圾收集带来了压力。
  • 编译器可以为简单的连接生成“stringbuilderlike”代码
  • FOR 实际上可能更慢,因为它可能需要边界检查,而 foreach 循环没有完成,因为编译器“知道”它们在边界内。
于 2012-09-11T14:48:10.357 回答
2

这取决于Cursor提供给函数的 s 数量。

StringBuilder在连接 4-10 个字符串时,这两种方法之间的大多数比较似乎都优于字符串连接。StringBuilder如果我没有明确的理由,我很可能会赞成(例如,我的问题/应用程序的两种方法的性能比较)。我会考虑在 中预先分配一个缓冲区StringBuilder以避免(许多)重新分配。

请参阅字符串连接与字符串生成器。Performance and Concatenating with StringBuilders vs. Strings讨论这个主题。

于 2012-09-11T16:47:00.150 回答
1

你做了多少串联?如果很多,请使用 StringBuilder。如果只有几个,那么创建 StringBuilder 的开销将超过任何优势。

于 2012-09-11T14:43:44.713 回答
1

我会把我的钱放在

           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 的更新放在一起。查看他答案中的链接。

于 2012-09-11T15:46:47.590 回答