"Не используйте 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?
Ответы
Ответ 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. Посмотрите ссылку в своем ответе.