Comments 38
На .NET 2.0 beta 2 результаты несколько лучше, но не так уж и сильно.
К теме статьи не относится, но .NET пора обновить)
+21
да, статья писалась автором довольно давно, но, полагаю, актуальности ещё не утратила
+1
Дочитав до этой строки сразу бросил читать.
ЗЫ: проблему можно решить по хард-кору через указатели (unsafe code) отключив интернирование чтобы не получить неожиданные результаты :-) (кто воспримет это серьёзно — тот зануда).
ЗЫ: проблему можно решить по хард-кору через указатели (unsafe code) отключив интернирование чтобы не получить неожиданные результаты :-) (кто воспримет это серьёзно — тот зануда).
-8
UFO just landed and posted this here
Это перевод старющей статьи Скита. Глаза разувайте перед тем как ляпать что-то.
0
немного огорчает в StringBuilder отсутствие перегрузок, например sb += «append»;
0
Часто можно еще делать примерно так:
Понятно что массив может быть чего-то поинтереснее int-ов, и вместо ToString() что-то посложнее. Также можно использовать string.Join если нужно разделить значения, например, запятыми.
Удобно для простых сценариев, типа пройтись по коллекции и как-нибудь слепить из элементов строку.
По производительности не проверял, но по логике должно работать примерно как StringBuilder.
int[] someArray = {1,2,3};
string s = string.Concat(someArray.Select(i => i.ToString()));
Понятно что массив может быть чего-то поинтереснее int-ов, и вместо ToString() что-то посложнее. Также можно использовать string.Join если нужно разделить значения, например, запятыми.
Удобно для простых сценариев, типа пройтись по коллекции и как-нибудь слепить из элементов строку.
По производительности не проверял, но по логике должно работать примерно как StringBuilder.
0
Имхо, это даже быстрее чем если сделать:
foreach (var item in someArray) stringBuilder.Append(item.ToString());
т.к. string.Concat сразу создаст строку нужной длины через FastAllocateString и FillStringChecked-ами её заполнит.
foreach (var item in someArray) stringBuilder.Append(item.ToString());
т.к. string.Concat сразу создаст строку нужной длины через FastAllocateString и FillStringChecked-ами её заполнит.
0
Там все-таки надо еще массив сделать и переложить все в него. Как на практике будет — сложно сказать, надо мерить. Впрочем, тут уже речь процентах, так что можно смело применять.
0
Да, но если речь идёт о сферических тестах, то вот такой paste.org.ru/?tllr43 даёт интересные результаты: быстрее всего string.Join, потом String.Concat, a потом уже StringBuilder.
String.Join использует хитрый UnSafeCharBuffer.
String.Join использует хитрый UnSafeCharBuffer.
0
А если у вас IEnumerable с неизвестным заранее количеством?
Достаточно семантично, и крайне быстро (а при желании можно состряпать «сахарное» расширение):
var result = someArray.Aggregate(new StringBuilder(), (a,x) => a.Append(x)).ToString();
Достаточно семантично, и крайне быстро (а при желании можно состряпать «сахарное» расширение):
var result = someArray.Aggregate(new StringBuilder(), (a,x) => a.Append(x)).ToString();
0
А я знаю способ еще лучше. Дать всем в этой ветке комментов сначала премию за нестандартное мышление, а потом по ушам за извращения, ведущие к нечитаемому и несопровождаемому коду.
А если окажется что выгода от извращений < 0.1%, то лишить премии.
Итого: премии ноль, уши горят.
А если окажется что выгода от извращений < 0.1%, то лишить премии.
Итого: премии ноль, уши горят.
+1
ну покажите нам пример генерации md5 hash с результатом в string hex _без извращений_
+1
Тут согласен. Хотя время на любой способ сборки строки по сравнению с вычислением MD5 ничтожно, но если вычислять хеш от хеша в цикле, то становится интересно.
Может знаете, раньше было такое кунг-фу при проверке пароля на локальном компьютере, много раз хеш от хеша — специально, чтобы проверка пароля занимала минимум секунду — это делало простой брутфорс перебором нереальным.
Может знаете, раньше было такое кунг-фу при проверке пароля на локальном компьютере, много раз хеш от хеша — специально, чтобы проверка пароля занимала минимум секунду — это делало простой брутфорс перебором нереальным.
0
Насчет удобочитаемости кода с билдером.
А насчет того, что стоит ли вообще пытаться оптимизировать такое место, уже столько копий сломано…
string name = new StringBuilder(firstName).Append(" ").Append(lastName).ToString();
А насчет того, что стоит ли вообще пытаться оптимизировать такое место, уже столько копий сломано…
0
Много раз натыкался на подобные статьи/обсуждения.
Лично я не понимаю, как можно использовать язык программирования и не иметь представления об изменяемости/неизменяемости строк и асимптотике операций над ними.
Лично я не понимаю, как можно использовать язык программирования и не иметь представления об изменяемости/неизменяемости строк и асимптотике операций над ними.
0
Неплохо, детально, но зачем?
0
На деле работа со строками очень может даже стать узким местом, особенно при разборе текста — например парсером.
Из своего опыта скажу, разительно помогло «не создавать новых строк», т.е. везде где можно использовалась ссылка на оригинальный текст и передавались индексы начала и конца фрагмента.
Благо в .NET есть порядочно методов, которые работают именно с подстроками.
Из своего опыта скажу, разительно помогло «не создавать новых строк», т.е. везде где можно использовалась ссылка на оригинальный текст и передавались индексы начала и конца фрагмента.
Благо в .NET есть порядочно методов, которые работают именно с подстроками.
0
UFO just landed and posted this here
UFO just landed and posted this here
Мне кажется у вас ошибка в тесте — вы забыли про строчку «string result = x.ToString();». Ведь именно в ней происходит конкатенация.
+1
.net 4.0, тестим string.Format, string.Concat и конкатенацию плюсом:
Тестим конкатенацию билдером:
Так что похоже любой метод конкатенации приводит к практически одинаковым результатам. В данном случае, основное время было затрачено на a.ToString() и b.ToString() (явный или неявный).
Короче, используйте то, что вам удобно, и не парьтесь. Только не используйте string.Format() — он в полтора раза медленнее, что и понятно — парсинг строки формата и подстановка параметров занимает время.
for (int i = 0; i < 10000000; i++)
{
var a = rnd.Next(10000);
var b = rnd.Next(1000);
//string s = string.Format("{0} {1}", a, b); // 9.9 seconds
//string s = string.Concat(a, " ", b); // 6.5 seconds
string s = a.ToString() + " " + b.ToString(); // 6.8 seconds
builder.Append(s);
}
Тестим конкатенацию билдером:
for (int i = 0; i < 10000000; i++)
{
var a = rnd.Next(10000);
var b = rnd.Next(1000);
// 5.9 seconds
builder.Append(a);
builder.Append(" ");
builder.Append(b);
}
Так что похоже любой метод конкатенации приводит к практически одинаковым результатам. В данном случае, основное время было затрачено на a.ToString() и b.ToString() (явный или неявный).
Короче, используйте то, что вам удобно, и не парьтесь. Только не используйте string.Format() — он в полтора раза медленнее, что и понятно — парсинг строки формата и подстановка параметров занимает время.
0
Такой скачек производительности достигается благодаря устранению ненужной операции копирования — копируются только те данные, которые присоединяются к результирующей строке.
Сдается мне, что автор не прав. И скачек производительности достигается тем, что память выделяется гораздо реже.
+1
Нет, Скит прав. Выделение памяти — это копеечная операция.
0
С каких это пор выделение памяти стало копеечной операцией? По опыту — это самая дорогостоящая операция из всех возможных. Тем более, что вы никогда не знаете сколько точно она займет времени.
Вот копирование — это действительно копеечная операция — как два байта переслать :)
Вот копирование — это действительно копеечная операция — как два байта переслать :)
0
Там используется какой то супербыстрый алгоритм выделения памяти fast memory allocation к тому же оптимизированный под строки небольшого назмера. Посмотрите рефлектором.
В дот нете выделение памяти вообще очень быстрая операция — там не нужно искать куда ее выделить благодаря работе дефрагментатора памяти. И вы точно знаете сколько времени это займет — почти ноль времени. В тоже время копирование остается копированием.
Но создание временных элементов в большом количестве быстро приведет к переполнению управляемой кучи.
А в случае больших строк они еще и попадают в конце концов Large Object Heap — в котором нет дефрагментации памяти при сборке мусора и последняя будет происходить скорее всего непрерывно, так как механизм выделения памяти остается прежним.
Поэтому выигрыш не за счет копирования может даже или выделения памяти — а просто за счет менее активной работой с управляемой кучей.
Но вообще то, если строк там тысячи конкатенируются — это актуально. Задача, которая встречается оооочень редко — к тому же решается обычно вообще избавлением от работы со строками.
Для практических задач обычных конкатенаций строк никакие стринг билдеры не нужны — все это детские шалости и экономия на спичках.
В дот нете выделение памяти вообще очень быстрая операция — там не нужно искать куда ее выделить благодаря работе дефрагментатора памяти. И вы точно знаете сколько времени это займет — почти ноль времени. В тоже время копирование остается копированием.
Но создание временных элементов в большом количестве быстро приведет к переполнению управляемой кучи.
А в случае больших строк они еще и попадают в конце концов Large Object Heap — в котором нет дефрагментации памяти при сборке мусора и последняя будет происходить скорее всего непрерывно, так как механизм выделения памяти остается прежним.
Поэтому выигрыш не за счет копирования может даже или выделения памяти — а просто за счет менее активной работой с управляемой кучей.
Но вообще то, если строк там тысячи конкатенируются — это актуально. Задача, которая встречается оооочень редко — к тому же решается обычно вообще избавлением от работы со строками.
Для практических задач обычных конкатенаций строк никакие стринг билдеры не нужны — все это детские шалости и экономия на спичках.
+1
Ну для дотнета это справедливо с тех самых пор, как появился сам дотнет :).
-1
В Java конкатенация строк оптимизируется на уровне компилятора. При этом в Java 2.0 компилятор подставляет StringBuffer, а в Java 5.0 компилятор оптимизирует конкатенацию строк с помощью StringBuilder.
0
UFO just landed and posted this here
Практически всегда, если явно не используются методы ручной оптимизации на основе того или иного варианта String.concat().
0
Чтобы обогнать StringBuilder, нужно как-то сделать так, чтобы каждый символ копировался в среднем меньше 2.5 раз. Например, когда ограничение на длину итоговой строки заранее известна. Одно копирование — из исходных строк в char[], другое — при создании строки из фрагмента массива. Тогда у StringBuilder (с переданной конструктору той же Capacity, чтобы было честно) удается выиграть примерно 20% скорости (при благоприятных условиях, когда строки возникают в том же цикле, где они кладутся в массив). Стоит сделать хотя бы один Resize — и уже проигрыш.
На 10^8 строк средней длиной 5 символов: построение через StringBuilder — 2.6 сек, а через массив — 2.2 сек. (.NET 4.0)
Выиграть можно было бы, если как-нибудь захватить char[], не обнуляя его — так не дадут :(
На 10^8 строк средней длиной 5 символов: построение через StringBuilder — 2.6 сек, а через массив — 2.2 сек. (.NET 4.0)
Выиграть можно было бы, если как-нибудь захватить char[], не обнуляя его — так не дадут :(
0
Sign up to leave a comment.
Эффективная конкатенация строк в .NET