|
Медленное сложение строк | ☑ | ||
---|---|---|---|---|
0
PitNN
24.08.16
✎
12:07
|
Добрый день, коллеги.
Возникла непонятная ситуация. Сложение строк на тороне сервера происходит очень долго. Конкретно: Стр = стр + "('" + СтрокаСоотношения.ГУИД + "', '"; Данная операция выполнившись 152899 раз заняла 72,663834 секунды и находится на первом месте в замере производительности. Вторая в списке тяжелых конструкций: Стр = стр + "', '" + ИДПрайса + "')"; Соответственно 152899 раз и 72,093324 секунд. Данное с Отдельное тестирование сложений тысячи строк из ста символов выдает что данная операция делается меньше секунды. С чем может быть связано подобное поведение? |
|||
1
Lexey_
24.08.16
✎
12:08
|
(0) с обращением через точку
|
|||
2
Nuobu
24.08.16
✎
12:09
|
С преобразованием типов.
|
|||
3
PitNN
24.08.16
✎
12:11
|
(1) Не согласуется с второй строкой, когда обращения через точку отсутствует
|
|||
4
PitNN
24.08.16
✎
12:12
|
(2) ИДПрайса Изначально имеет тип "Строка"
|
|||
5
Nuobu
24.08.16
✎
12:12
|
Отдельное тестирование сложений тысячи строк из ста символов выдает что данная операция делается меньше секунды.
А теперь "меньше секунды" умножь на 152. Ибо ты тестировал тысячу, а у тебя - 152 000. |
|||
6
hitodom
24.08.16
✎
12:14
|
сложение строк не сильная сторона 1с. попробуй записьтекста
|
|||
7
PitNN
24.08.16
✎
12:15
|
(5) Неправильно написал, виноват. Там идет цикл 150000 раз.
Собирается строка из строк по сто символов. На каждой тысячной итерации строка обнуляется. Вот это полное действие делается меньше секунды |
|||
8
WebberNSK
24.08.16
✎
12:16
|
(0) http://speshuric.livejournal.com/163665.html
рекомендую почитать |
|||
9
PitNN
24.08.16
✎
12:20
|
Вот часть кода:
ТекДатаВремя = Формат(ТекущаяДата(),"ДФ=yyyy-MM-dd")+" "+Формат(ТекущаяДата(),"ДФ=ЧЧ:мм:сс"); Ошибка = ""; НужнаЗапятая = Ложь; сч = 0; стр = "INSERT INTO c_lees (`guid`, `partner`, `guidsupplier`, `daysdelivery`, `guidstorage`, `quantity`, `date`, `id_price_list`) VALUES "; Поставщик = Соглашение.Контрагент.Наименование; ПоставщикГУИД = Соглашение.Контрагент.ГУИД; ДнейДоставки = 0; СкладГУИД = "e73df8ac-f2f8-11e4-8673-3085a93da151"; Если Поставщик <> "трам-там-там" Тогда ДнейДоставки = Строка(Соглашение.СрокПоставки); СкладГУИД = "ad412042-3eeb-11e3-a566-001e6707b110"; КонецЕсли; Для каждого ВыборкаОстатков Из ИзмененныеПозиции Цикл Если НужнаЗапятая Тогда Стр = Стр + ","; КонецЕсли; Стр = стр + "('"+ВыборкаОстатков.НоменклатураГУИД; Стр = стр + "', '"+Поставщик + "', '"; Стр = стр + ПоставщикГУИД + "', '"; Стр = стр + ДнейДоставки + "', '"; Стр = стр + СкладГУИД + "', '"; Стр = стр + ?(ВыборкаОстатков.ВНаличии < 0.5, Окр(ВыборкаОстатков.ВНаличии), Формат(Окр(ВыборкаОстатков.ВНаличии), "ЧГ=0")); Стр = стр + "', '" + ТекДатаВремя + "', '"; Стр = стр + ИДПрайса + "')"; сч = сч+1; Если сч >= 1000 Тогда сч = 0; стр = "INSERT INTO c_lees (`guid`, `partner`, `guidsupplier`, `daysdelivery`, `guidstorage`, `quantity`, `date`, `id_price_list`) VALUES "; НужнаЗапятая = Ложь; КонецЕсли; КонецЦикла; Как видно переменная ТекДатаВремя изначально имеет тип строка, но при этом Стр = стр + "', '" + ТекДатаВремя + "', '"; выполнившись 50000 раз затратило 29,4 секунд. Это к вопросу о приведении типов |
|||
10
PitNN
24.08.16
✎
12:22
|
(8) Спасибо, изучаю
|
|||
11
Serginio1
24.08.16
✎
12:22
|
Используй ЗаписьXML.ЗаписатьБезОбработки(стр);
Пример можно посмотреть здесь http://catalog.mista.ru/public/544232/ |
|||
12
Fragster
гуру
24.08.16
✎
12:23
|
(11) прав.
|
|||
13
hitodom
24.08.16
✎
12:23
|
А просто ЗаписьТекста хуже?
|
|||
14
ptiz
24.08.16
✎
12:24
|
(0) Чем больше строка - тем дольше добавление.
|
|||
15
ptiz
24.08.16
✎
12:28
|
ЗаписьТекста (добавление без перевода строки) + ЧтениеТекста поможет
|
|||
16
Мыш
24.08.16
✎
12:35
|
Стр = Стр + "текст";
Особенности работы с памятью. Область памяти под переменную динамически увеличивается. Накладные расходы на эту операцию составляют основное время, имхо. |
|||
17
Fragster
гуру
24.08.16
✎
12:36
|
(13) вроде она через память не работает, а только через файл
|
|||
18
Jija Grenkov
24.08.16
✎
12:41
|
(16) скорее всего нечего не увеличивается. Просто стр и "текст" теряют ссылки и уходят в муссор, а под новую итоговую строку выделятся нужный объем памяти. И так очень много раз. От чего сборщик муссора пыхтит. Скорее всего и процесс отъедает ядро проца полностью
|
|||
19
Мыш
24.08.16
✎
12:42
|
(18) Тоже вариант. Точно только у разработчиков платформы узнать можно )
|
|||
20
Fragster
гуру
24.08.16
✎
12:43
|
(18) стр = стр + стр в цикле очень быстро сжирает память, так как ищет непрерывный кусок для результата, даже сборщик мусора не помогает. ну и тормозит.
|
|||
21
apokrit
24.08.16
✎
12:47
|
(0) Если версия платформы + режим совместимости позволяют, можно использовать СтрСоединить. Метод специально для этого и делали.
|
|||
22
Serginio1
24.08.16
✎
12:53
|
(13) Здесь есть сравнение
http://speshuric.livejournal.com/163665.html |
|||
23
DrZombi
гуру
24.08.16
✎
12:59
|
(22) Какая любопытненькая статейка :)
|
|||
24
Мыш
24.08.16
✎
13:17
|
(21) Сравнил, кстати. ЗаписьXML и СтрСоединить() сравнимы по скорости )
|
|||
25
PitNN
24.08.16
✎
15:45
|
(11) Да, действительно, на первое место вылезли уже другие операции. Огромное спасибо) Как говорится век живи - век учись)
|
|||
26
PitNN
24.08.16
✎
15:50
|
Подскажите еще, пожалуйста. Что эффективнее, выполнение одной большой инструкции Соединение.Execute(ТекстЗапросовInsert);
или нескольких поменьше? |
|||
27
Fragster
гуру
24.08.16
✎
16:01
|
(26) есть нюансы
|
|||
28
Fragster
гуру
24.08.16
✎
16:02
|
можно вообще отправить большой инсерт асинхронно и дальше работать
|
|||
29
PitNN
24.08.16
✎
16:08
|
(27) Что за нюансы, подскажи пожалуйста
|
|||
30
PitNN
24.08.16
✎
16:09
|
(28) Как это выглядит?
|
|||
31
orefkov
24.08.16
✎
16:41
|
Сборщик мусора в 1С? Отличные грибы!
|
|||
32
b_ru
24.08.16
✎
16:45
|
(31) А что, в 1С его нету?
|
|||
33
Смотрящий
24.08.16
✎
16:46
|
(30) Для начала покупаешь IIS
|
|||
34
orefkov
24.08.16
✎
16:50
|
(32)
Нет конечно. Только счётчик ссылок в каждом объекте. Память освобождается сразу при освобождении объекта. Циклические ссылки висят до смерти процесса. |
|||
35
aka AMIGO
24.08.16
✎
17:02
|
(34) Кто/Что заботится об очистке памяти? 1С? или всё-таки ОСь?
|
|||
36
b_ru
24.08.16
✎
17:04
|
(34) Мне почему-то казалось, что это простейшая реализация сборки мусора, но да, по формальному определению оно ей не является :(
|
|||
37
PitNN
24.08.16
✎
18:14
|
Сделал отправку одного большого запроса на сервер. Стало быстрее чем несколько меньших
|
|||
38
Jija Grenkov
24.08.16
✎
21:47
|
(34) получается и на сервере такой подход? Тогда понятно откуда неизбежные утечки памяти. Еще скорее всего нет никаких механизмов дефрагментации памяти так как этим обычно занимается GC
(36) По идее это было GC, если бы запускалось в отдельном процессе, а тут видимо interceptor в тех местах где пропадает ссылка. На сколько помню в objective C похожий подход. |
|||
39
Torquader
24.08.16
✎
22:02
|
(38) Это не сборка мусора, а процесс управления кучей памяти.
Он ничего никуда не перемещает, а просто или выделяет память или освобождает. Что же касается строк, то упирается всё не в выделение памяти, а в копирование предыдущего значения строки, в новое место. Причём, со сложением строк в javascript те же "тапки" - тормозит так, что хочется в экран плюнуть. |
|||
40
Jija Grenkov
24.08.16
✎
22:18
|
(39) почти везде так будет где строки иммутабельны. К примеру в джаве есть мутабельная и иммутабельная строка.
|
|||
41
Jija Grenkov
24.08.16
✎
22:25
|
(39) в JS можно использовать массив, должно быть быстрее
|
|||
42
Torquader
24.08.16
✎
22:54
|
Проблема в том, что когда складываешь две строки, нужно, чтобы под результат была выделена память - и в момент выделения памяти потребуется перенести блок данных из одного места в другое - всё равно будет memcpy только уже на уровне выделения памяти.
|
|||
43
Jija Grenkov
25.08.16
✎
00:08
|
(42) не все так однозначо. Когда мы в цикле складываем 2 переменных типа строка и присваиваем - это значение 1-й переменной,
for (var i=0; i<10000; i++){ Var a += "foo"; } то выделяется область под результирующую строку куда копируються данные 2-х строк источников и так много раз. И GC тратит ресурс на освобождение памяти от 2-х исходных строк. Когда используется буфер, то в нем заранее выделена память под всю результирующую строку. В случае с JS некое подобие работы с буфером это создать массив где лежать все кусочки строки и 1 операцией сцепить все строки. В таком случае интерпритатору очень просто определить размер результирующей строки. var arr = [] for (var i=0; i<10000; i++){ arr.push("foo"); } arr.join("") ps. Сейчас браузеры пытаются оптимизировать 1й вариант и не везде получится увидеть разницу. |
|||
44
H A D G E H O G s
25.08.16
✎
00:50
|
(43) Сам то понял, что сказал?
|
|||
45
Jija Grenkov
25.08.16
✎
01:03
|
(44) естественно, если не ясно детальное объяснение, скажу короче. В 1 варианте операций выделения памяти и сборки мусора больше.
|
|||
46
Jija Grenkov
25.08.16
✎
01:17
|
1 вариант:
- В каждой итерации выделится и станет "муссором" память под "foo". - Каждую итерацию будет выделена и станет "муссором" память под состояние переменной "а". Размер переменной "а" будет увеличиваться с каждой итерацией. 2 вариант: - В каждой итерации выделится память под "foo". - 1 раз после цикла выделится память под итоговую строку после чего память выделенная под "foo" станет "муссором" |
|||
47
H A D G E H O G s
25.08.16
✎
02:12
|
(46) Проблема очистки стоит в этих ваших виртуальных машинах. В наших православных Дельфи проблемы очистки нет. Есть проблемы утечки. Которую контролят спецутилиты на этапе разработки.
Что до строк, то есть 3 стратегии конкатенации: 1) Реаллокация на каждый чих 2) Программер сам знает (знает только программер, менеджер памяти никогда этого не знает) какой длины у него результирующая строка. Он выделяет память сразу и пишет в нее. Простейший пример BASE64 с ее 4/3 размером. 3) Мудрый менеджер памяти/среда разработки выделяет память под строки блоками, байт по 8000, к примеру, как это делает TStream в Дельфи. Самый годный вариант, кстати. Но я бы буфер сделал бы больше, по 64 Кбайта норм. Это все, что нужно знать в этой теме. Все остальное - лишнее. |
|||
48
Jija Grenkov
25.08.16
✎
02:43
|
(47) Обджект паскаль может и был не плохим языком, но факт остается фактом, что этот ЯП самоликвидировался и именно из за конкуренции с языками работающими на виртуальной машине(java, c#).
В джаве нет таких проблем, там и оптимизатор умный и девелопер может использовать StringBuilder и сам указать capacity этого билдера/буфера. Есть область памяти называемая пулом стрингов, где строки могут кешироваться и при повторном использовании литерала память не будет выделяться (в примере для JS под "foo" память выделится 1 раз) В джава так же есть возможность напрямую управлять памятью и эта память будет не под управлением виртуальной машине, мы такое в бигдате юзаем. |
|||
49
Garykom
гуру
25.08.16
✎
03:12
|
(48) Да удобство работы с динамическим выделением памяти в объектном паскале не очень по сравнению с Java/C#.
Требует более высокого скилла прогов (как и C++), но конечный результат для пользователей чреват требованием апгрейда железа. |
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |