Фрагментация памяти при объединении или добавлении строк, но не с помощью строки. Формат?
Итак, профессор в университете только что сказал мне, что использование конкатенации строк в C# (т.е. когда вы используете оператор знака плюс) создает фрагментацию памяти, и что я должен использовать string.Format
вместо.
Теперь я много искал в переполнении стека и обнаружил множество потоков о производительности, которые объединяют строки и выигрывают. (Некоторые из них включают это, это и это)
Я не могу найти кого-то, кто говорит о фрагментации памяти, хотя. Я открыл.NET string.Format
используя ILspy и, по-видимому, он использует тот же построитель строк, что и string.Concat
метод делает (что, если я понимаю, это то, что +
знак перегружен). На самом деле: он использует код в string.Concat
!
Я нашел эту статью с 2007 года, но я сомневаюсь, что она точна сегодня (или когда-либо!). Очевидно, компилятор достаточно умен, чтобы избежать этого сегодня, потому что я не могу воспроизвести проблему. И добавление строк со знаком string.format и знаком плюс приводит к тому, что внутри используется один и тот же код. Как было сказано ранее, string.Format использует тот же код string.Concat использует.
Так что теперь я начинаю сомневаться в его претензии. Это правда?
2 ответа
Итак, профессор в университете только что сказал мне, что использование конкатенации строк в C# (т.е. когда вы используете оператор знака плюс) создает фрагментацию памяти, и что вместо этого я должен использовать string.Format.
Нет, вместо этого вам следует провести пользовательское исследование, установить ориентированные на пользователя метрики реальной производительности и измерить производительность вашей программы по этим метрикам. Когда и только когда вы обнаружите проблему с производительностью, вы должны использовать соответствующие инструменты профилирования, чтобы определить причину проблемы с производительностью. Если причиной является "фрагментация памяти", устраните ее, выявив причины "фрагментации" и попытавшись провести эксперименты, чтобы определить, какие методы смягчают эффект.
Производительность не достигается с помощью "советов и хитростей", таких как "избегать конкатенации строк". Производительность достигается путем применения инженерной дисциплины к реалистичным задачам.
Чтобы решить вашу более конкретную проблему: я никогда не слышал совета отказаться от конкатенации в пользу форматирования по соображениям производительности. Обычно дается совет избегать повторной конкатенации в пользу строителей. Итеративная конкатенация является квадратичной по времени и пространству и создает давление сбора. Строители выделяют ненужную память, но линейны в типичных сценариях. Ни один из них не создает фрагментацию управляемой кучи; итеративная конкатенация приводит к образованию непрерывных блоков мусора.
Количество раз, когда у меня возникала проблема с производительностью, которая сводилась к ненужной фрагментации управляемой кучи, составляет ровно один; в ранней версии Roslyn у нас был шаблон, в котором мы выделяли маленький долгоживущий объект, затем маленький недолговечный объект, затем маленький долгоживущий объект... несколько сотен тысяч раз подряд, и в результате получалась максимально фрагментированная куча вызывал проблемы с производительностью, влияющие на пользователей в коллекциях; мы определили это путем тщательного измерения производительности в соответствующих сценариях, а не путем специального анализа кода с наших удобных стульев.
Обычный совет не избегать фрагментации, а избегать давления. Во время проектирования Roslyn мы обнаружили, что давление оказало гораздо большее влияние на производительность GC, чем фрагментация, как только наша вышеупомянутая проблема с моделью распределения была решена.
Я советую вам либо попросить объяснения у вашего профессора, либо найти профессора, который придерживается более дисциплинированного подхода к показателям эффективности.
Теперь все, что сказано, вы должны использовать форматирование вместо конкатенации, но не по соображениям производительности. Скорее, для удобочитаемости кода, локализуемости и подобных стилистических проблем. Строка формата может быть преобразована в ресурс, она может быть локализована и т. Д.
Наконец, я предупреждаю вас, что если вы объединяете строки, чтобы создать что-то вроде SQL-запроса или блока HTML, предназначенного для обслуживания пользователя, то вы не захотите использовать ни один из этих методов. Эти приложения построения строк имеют серьезные последствия для безопасности, если вы ошибаетесь. Используйте библиотеки и инструменты, специально предназначенные для создания этих объектов, вместо того, чтобы создавать свои собственные строки.
Проблема с конкатенацией строк заключается в том, что строки являются неизменяемыми. Строка1 + строка2 не объединяет строку2 в строку1, она создает новую строку. Использование StringBuilder (или string.Format) не имеет этой проблемы. Внутри StringBuilder содержит символ char[], который он перераспределяет. Добавление чего-либо к StringBuilder не создает никаких новых объектов, если ему не хватает места в char[] (в этом случае он перераспределяет новый).
Я провел быстрый тест. Я думаю, что это доказывает суть:)
StringBuilder sb = new StringBuilder();
string st;
Stopwatch sw;
sw = Stopwatch.StartNew();
for (int i = 0 ; i < 100000 ; i++)
{
sb.Append("a");
}
st = sb.ToString();
sw.Stop();
Debug.WriteLine($"Elapsed: {sw.Elapsed}");
st = "";
sw = Stopwatch.StartNew();
for (int i = 0 ; i < 100000 ; i++)
{
st = st + "a";
}
sw.Stop();
Debug.WriteLine($"Elapsed: {sw.Elapsed}");
Консольный вывод:
Прошло: 00:00:00.0011883 (StringBuilder.Append())
Прошло: 00:00:01.7791839 (+ оператор)