Подтвердить что ты не робот

"Не используйте StringBuilder или foreach в этом горячем кодовом пути"

Я просматриваю исходный код проекта Open Source SignalR, и я вижу этот код diff, который озаглавлен "Не используйте 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 иногда может быть менее эффективным, и почему он заменяется на.

Однако я узнал и испытал, что StringBuilder - самый эффективный способ конкатенации строк. Поэтому мне интересно, почему автор решил заменить его на стандартную конкатенацию.

Что здесь не так и вообще об использовании StringBuilder?

4b9b3361

Ответ 1

Я сделал изменение кода, и да, он внес огромный вклад в количество распределений (GetEnumerator()) вызывает vs not. Представьте, что этот код миллионы раз в секунду. Количество выделенных счетчиков смехотворно и их можно избежать.

изменить: Теперь мы инвертируем управление, чтобы избежать каких-либо распределений (прямое письмо писателю): https://github.com/SignalR/SignalR/blob/2.0.2/src/Microsoft.AspNet.SignalR.Core/Messaging/Cursor.cs#L36

Ответ 2

Просто надейтесь, что парень, который изменил это, фактически измерил разницу.

  • При создании экземпляра новой строковой сборки каждый раз накладные расходы. Это также оказывает давление на сбор памяти/мусора.
  • Компилятор может генерировать "строковый" код для простых конкатенаций
  • FOR может быть на самом деле медленнее, потому что может потребоваться проверка границ, которая не является сделанные с помощью циклов foreach, поскольку компиляторы "знают", что они находятся в пределах границ.

Ответ 3

Это зависит от количества Cursor, предоставляемого функции.

Большинство сравнений между двумя apporaches, по-видимому, благоприятствуют StringBuilder по конкатенации строк при конкатенации 4-10 строк. Я бы скорее всего поддержал StringBuilder, если бы у меня не было явных причин не слишком (например, сравнение производительности двух подходов для моей проблемы/приложения). Я бы предпочел предварительно выделить буфер в StringBuilder, чтобы избежать (много) перераспределений.

См. Конкатенация строк против String Builder. Производительность и Конкатенация с StringBuilders vs. Strings для обсуждения этой темы.

Ответ 4

Сколько конкатенаций вы делаете? Если их много, используйте StringBuilder. Если всего несколько, то накладные расходы на создание StringBuilder превысят любое преимущество.

Ответ 5

Я положу деньги на

           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. Посмотрите ссылку в своем ответе.