19.01.2017, 13:18 | #1 |
Участник
|
Помогите найти: Сравнение производительности различных операндов при конкатенации строк
Я почему-то уверен, что когда-то писал небольшой проектик, который сравнивал производительность и память требуемую для выполнения конкатенации строк различными способами:
1) s1 = 'string'; s1 += s2; s1 += s3; 2) s1 = 'string'; s1 = s1 + s2 + s3; 3) s1 = 'string'; s1 = strFmt('%1%2%3', s1, s2, s3); Может кто найдет или создаст заново? Очень хочется посмотреть, что стоит использовать в АХ 7 Спасибо |
|
|
За это сообщение автора поблагодарили: Logger (1). |
19.01.2017, 13:56 | #2 |
Moderator
|
Учитывая ситуацию с продажами 7ки, данное сравнение представляется мне несколько бессмысленным. С равным успехом можно про FoxPro статью написать или про GNU Ada...
|
|
|
За это сообщение автора поблагодарили: Diman (1), ax_mct (0). |
19.01.2017, 14:12 | #3 |
Участник
|
Цитата:
Мне еще попадалось в коде когда много текста добавлялось к строке при помощи класса TextBuffer. Многократно добавлялись кусочки текста вызовом TextBuffer.AppendText(). Может включить в тест и такой способ ? Последний раз редактировалось Logger; 19.01.2017 в 14:17. |
|
19.01.2017, 14:40 | #4 |
Участник
|
Мне кажется что быстрее всего будет способ 1
Но ведь это совсем копейки. Чтобы что-то заметить надо блок кода с добавкой либо в цикле прокрутить либо завести десятки тысяч переменных которые и складывать. Хотя любые копейки в определенных условиях могут привести к тормозам Тормозит создание буфера. |
|
19.01.2017, 15:19 | #5 |
Участник
|
Вот что получилось у меня, на основании конкатенации 5 строк 20 тыщ раз
6.3. 7.0 В 6.3 разница была в 250!! раз В 7.0 разницы практически нет, с небольшим преимуществом операнда + |
|
|
За это сообщение автора поблагодарили: mazzy (2), alex55 (1). |
19.01.2017, 15:38 | #6 |
Участник
|
Если в 6.3 ранить в IL, то видим картину похожую на 7
Похоже что теперь += создает новую строку каждый раз. |
|
|
За это сообщение автора поблагодарили: mazzy (2), Logger (10). |
19.01.2017, 15:54 | #7 |
Участник
|
Полезные тесты, благодарю вас за них, Иван!
|
|
19.01.2017, 16:20 | #8 |
Banned
|
В .NET на больших массивах самое эффективное использовать StringBuilder.
Вполне логично предположить что TextBuffer его полный аналог, а с компиляцией в IL возможно что по сути тот же IL код. То есть рекомендации ниже должны подходить и для TextBuffer. Цитата:
Определённо используйте StringBuilder, когда вы конкатенируете строки в нетривиальном цикле, и особенно, когда вы не знаете (на момент компиляции), сколько именно итераций будет произведено. К примеру, чтение содержимого текстового файла путём считывания по одному символу внутри одной итерации в цикле, и конкатенация этого символа через оператор += предположительно «убьёт» ваше приложение в плане производительности.
Определённо используйте оператор +=, если вы можете указать все необходимые для конкатенации строки в одном утверждении. Если вам нужно конкатенировать массив строк, используйте явный вызов String.Concat, а если между этими строками нужен разделитель — используйте String.Join. https://habrahabr.ru/post/166701/ P.S. Кстати интересно стоимость вызова и использования System.Text.StringBuilder вместо TextBuffer. Если это ничего не стоит когда в IL, то может лучше сразу .NET использовать? Последний раз редактировалось ax_mct; 19.01.2017 в 16:27. |
|
|
За это сообщение автора поблагодарили: mazzy (2), Logger (3), Товарищ ♂uatr (1). |
19.01.2017, 16:27 | #9 |
Участник
|
Цитата:
А вот то что в 6.3 операция += работает медленнее под CIL чем p-code это очень неприятно. Может можно это пофиксить ? Наверняка можно создать более оптимальный код без создания новой строчки. Тем более что оператор += используется очень часто. Так глядишь в некоторых задачах p-code обгонит CIL. Последний раз редактировалось Logger; 19.01.2017 в 16:33. |
|
19.01.2017, 16:31 | #10 |
Участник
|
Мне тоже кажется что так лучше было бы. Я вообще думал что TextBuffer не покажет таких цифр производительности. Думал он вообще реализован как не IL объект и будут накладные расходы на вызов между IL кодом и обычным неуправляемым кодом.
|
|
19.01.2017, 16:34 | #11 |
Участник
|
скорее дело не в IL, а в сборщике мусора.
поддерживаю про StringBuilder. |
|
19.01.2017, 17:24 | #12 |
Участник
|
|
|
19.01.2017, 18:04 | #13 |
Участник
|
Поддерживаю про сборщик мусора, а также добавлю, что причиной служит то, что строки в .Net есть константа, и каждая итерация сильно нагружает память и в итоге она быстро заканчивается, запускается GC, который память освобождает и упаковывает. А вот переход на StringBuilder оправдан при большом кол-ве итераций изменений. Если посмотреть исходники StringBuilder, то там используетеся не тип string a char[], и память выделяется небольшими порциями.
Я вот все хочу сравнить производительность разных коллекций,все руки не доходят.
__________________
Sapere aude |
|
|
За это сообщение автора поблагодарили: Logger (3). |
19.01.2017, 18:57 | #14 |
Banned
|
Цитата:
Это хороший пойнт насчет того что что-то может и не переводиться в IL. Ни в чем нельзя быть уверенным Надо бы порыть насчет того что остается P-кодом при полной компиляции в CIL. И понять в этом контексте стоимость использования .NET обьектов в X++ коде. Последний раз редактировалось ax_mct; 19.01.2017 в 19:12. |
|
19.01.2017, 19:16 | #15 |
Banned
|
Из книги Inside Microsoft Dynamics AX 2012 R3 - Page 142.
NET interop When running X++ code as CIL, all conversion and marshaling between the runtimes are avoided. При этом же Mastering Microsoft Dynamics AX 2012 R3 Programming - Page 402 The form-level logic still uses p-code... То есть использование того же StringBuilder на клиентской стороне - возможно противопоказано так как возникнут накладные расходы. А вот в серверном коде с компиляцией в CIL - скорее всего показано. Есть ли в 7ке "клиентский" X++ код - этого я не знаю |
|
19.01.2017, 20:10 | #16 |
Участник
|
Цитата:
Вряд ли поэтому удастся сделать быстрый += в CIL. Получается, что как ни изворачивайся, а сложно теперь писать код одинаково быстрый и в p-code и в CIL. Ну если только TextBuffer или StringBuilder задействовать. Код по виду будет ближе к .Net а рекомендация BP использовать += вместо a=a+b теперь протухла. |
|
19.01.2017, 20:35 | #17 |
Участник
|
Цитата:
https://blogs.msdn.microsoft.com/mfp...-to-the-metal/ А в комментариях в ответ на претензию Цитата:
The T-Shirt lies. There is no such thing as fast X++ code. Please make X++ a full blown CLR language ASAP. Otherwise we are all doomed (a partner).
Цитата:
Thanks for the feedback alibertism, Your comment nicely demonstrates the core problem this post is trying to qualify. In my examples above all X++ code is running in CLR – the problem is not with the language.
Even if X++ was a full blown CLR language you would still face these problems. What we need to fix is not "just" the language and compiler – but all the libraries you are using when writing X++ code. In AX 2012 the majority of these are written in C++. To avoid the interop overhead we need to replace these with CLR implementations. This includes everything under System Documentation in the AOT, i.e >100 built-in functions, > 500 classes and the entire data access stack. Solving all this – and more is in the works for the next major release of AX – code named "Rainier". Именно поэтому я и сомневался в том, что скорость вызова TextBuffer будет высокой. Думал что он остался на неуправляемом коде. (Хотя как показал Майкл в статье, скорость бывает разной и в нашем случае видимо скорости вызова неуправляемого кода достаточна для быстрой работы) |
|
|
За это сообщение автора поблагодарили: ax_mct (10). |
19.01.2017, 21:11 | #18 |
Участник
|
|
|
19.01.2017, 23:32 | #19 |
Участник
|
|
|
20.01.2017, 11:38 | #20 |
Участник
|
X++: class RunnableClass1 { const int StringSite = 100000; public static Int64 PlusEquals() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Restart(); str s; for (int i = 1; i <= StringSite; i+=5) { s += int2Str(i); s += int2Str(i+1); s += int2Str(i+2); s += int2Str(i+3); s += int2Str(i+4); } var runtime = stopWatch.ElapsedMilliseconds; stopWatch.Stop(); return runtime; } public static Int64 Plus() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Restart(); str s; for (int i = 1; i <= StringSite; i+=5) { s = s + int2Str(i) + int2Str(i+1) + int2Str(i+2) + int2Str(i+3) + int2Str(i+4); } var runtime = stopWatch.ElapsedMilliseconds; stopWatch.Stop(); return runtime; } public static Int64 ThruStrFmt() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Restart(); str s; for (int i = 1; i <= StringSite; i+=5) { s = strFmt('%1%2%3%4%5%6', s, int2Str(i), int2Str(i+1), int2Str(i+2), int2Str(i+3), int2Str(i+4)); } var runtime = stopWatch.ElapsedMilliseconds; stopWatch.Stop(); return runtime; } public static Int64 ThruTextBuffer() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Restart(); TextBuffer tb = new TextBuffer(); for (int i = 1; i <= StringSite; i+=5) { tb.appendText(int2Str(i)); tb.appendText(int2Str(i+1)); tb.appendText(int2Str(i+2)); tb.appendText(int2Str(i+3)); tb.appendText(int2Str(i+4)); } tb.getText(); var runtime = stopWatch.ElapsedMilliseconds; stopWatch.Stop(); return runtime; } /// <summary> /// Runs the class with the specified arguments. /// </summary> /// <param name = "_args">The specified arguments.</param> public static void main(Args _args) { Box::info("START"); info(strFmt("+= took %1 ms", RunnableClass1::PlusEquals())); info(strFmt("+ took %1 ms", RunnableClass1::Plus())); info(strFmt("strFmt took %1 ms", RunnableClass1::ThruStrFmt())); info(strFmt("TextBuffer took %1 ms", RunnableClass1::ThruTextBuffer())); Box::info("DONE"); } } |
|
|
За это сообщение автора поблагодарили: Logger (5), Товарищ ♂uatr (1). |