При разработке высоконагруженных механизмов, таких как торговые боты, системы обработки больших прайс-листов или сложные расчеты в 1С, программисты часто сталкиваются с проблемой производительности. На первый взгляд, простой перебор строк в ТаблицаЗначений кажется элементарной операцией, но при миллионах итераций даже незначительные ошибки в архитектуре кода могут привести к фатальным задержкам. Разберем по шагам, почему код может работать медленно и как его ускорить.
Проанализируем ситуацию, представленную в обсуждении. Часто разработчики выполняют расчеты, используя переменные, которые одновременно являются реквизитами формы. В Обычных формах (и в некоторых сценариях Управляемых форм) каждое изменение значения такого реквизита внутри цикла заставляет систему инициировать перерисовку визуального интерфейса или обновлять состояние элементов формы. Если ваш цикл выполняется 200 000 раз, 1С попытается 200 000 раз обновить экран.
Рассмотрим пример кода, который демонстрирует колоссальную разницу в скорости:
// Медленный вариант: обращение к реквизиту формы
ТВ = ТекущаяУниверсальнаяДатаВМиллисекундах();
Для инд = 1 По 100000 Цикл
РеквизитФормы = РеквизитФормы + 1; // Заставляет форму перерисовываться
КонецЦикла;
Сообщить("Время выполнения с реквизитом: " + (ТекущаяУниверсальнаяДатаВМиллисекундах() - ТВ));
// Быстрый вариант: использование локальной переменной
ТВ = ТекущаяУниверсальнаяДатаВМиллисекундах();
ЛокальнаяПеременная = 0;
Для инд = 1 По 100000 Цикл
ЛокальнаяПеременная = ЛокальнаяПеременная + 1; // Работает в памяти
КонецЦикла;
РеквизитФормы = ЛокальнаяПеременная; // Присваиваем итог один раз
Сообщить("Время выполнения с переменной: " + (ТекущаяУниверсальнаяДатаВМиллисекундах() - ТВ));
Решение: Всегда выполняйте расчеты в локальных переменных, а результат присваивайте экранным полям только после завершения цикла. Это может ускорить выполнение кода в 30 и более раз.
В современных конфигурациях выполнение кода на клиенте ограничено мощностью рабочего места пользователя и скоростью передачи данных между клиентом и сервером. Проанализируем замер производительности: если вызов арифметических операций занимает значительное время, скорее всего, код выполняется в контексте клиентского приложения. Кроме того, при каждом вызове серверной процедуры с клиента передается "контекст" — все данные формы, что создает дополнительную нагрузку.
Решение: Переносите тяжелые вычисления на сервер. Используйте процедуры с директивой &НаСервереБезКонтекста. В этом случае на сервер будут переданы только те параметры, которые вы явно указали, что минимизирует объем трафика и ускоряет работу.
Выясним причину, по которой автор исходного вопроса использовал цикл. Если данные для обработки хранятся в РегистрСведений, наиболее эффективно будет использовать механизм запросов 1С. SQL-сервер оптимизирован для фильтрации и математических операций над большими наборами данных гораздо лучше, чем встроенный язык 1С.
Вместо того чтобы извлекать все записи из регистра, а затем фильтровать их условием Если стр.Вид = "Продажа" Тогда, следует перенести это условие в секцию ГДЕ запроса. Рассмотрим, как можно оптимизировать расчеты прямо в тексте запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КурсыВалют.КурсСделки КАК КурсСделки,
| КурсыВалют.Количество КАК Количество,
| ВЫБОР
| КОГДА КурсыВалют.Вид = ""Продажа"" ТОГДА КурсыВалют.Количество * КурсыВалют.КурсСделки * 0.99925
| ИНАЧЕ КурсыВалют.КурсВхода / КурсыВалют.КурсСделки * КурсыВалют.Количество * 0.99925
| КОНЕЦ КАК СуммаСКомиссией
|ИЗ
| РегистрСведений.КурсыВалют КАК КурсыВалют
|ГДЕ
| КурсыВалют.КурсСделки <= &ТекущийКурс";
Такой подход позволяет получить уже готовые, отфильтрованные данные, избавляя систему от необходимости перебирать миллионы строк в памяти 1С.
Если специфика задачи все же требует использования ТаблицаЗначений (например, когда данные динамически меняются в памяти), критически важно использовать индексацию. Если вы используете метод НайтиСтроки() или поиск по колонкам в цикле, время поиска без индекса растет линейно в зависимости от количества строк.
Посмотрим на пример создания индекса:
// Добавление индекса для ускорения поиска по колонкам "Вид" и "КурсСделки"
ТабОрдеров.Индексы.Добавить("Вид, КурсСделки");
// Теперь поиск будет работать практически мгновенно
ПараметрыОтбора = Новый Структура("Вид", "Продажа");
НайденныеСтроки = ТабОрдеров.НайтиСтроки(ПараметрыОтбора);
Рассмотрим еще несколько важных нюансов, которые помогут ускорить ваш код:
Стр.КурсСделки требует времени на обращение к свойству объекта. Если значение используется в формуле 3-4 раза, один раз прочитайте его в локальную переменную в начале итерации цикла.Для каждого. Это крайне медленно, так как после каждого удаления таблица перестраивается. Правильнее либо обходить таблицу с конца (используя индекс), либо помечать строки на удаление и удалять их массово, либо создавать новую таблицу и копировать туда только нужные данные.ТаблицаЗначений программно всегда указывайте типы колонок (объект ОписаниеТипов). Работа с нетипизированными данными в 1С всегда медленнее, так как системе приходится тратить ресурсы на определение типа значения при каждой операции.0.00075 за пределами цикла и просто умножайте на нее. Операция деления в процессоре выполняется дольше операции умножения.Подводя итог, можно сказать, что главная "ловушка" производительности в 1С кроется в смешивании логики интерфейса и математических вычислений. Вынесение расчетов на сервер, использование локальных переменных и максимальное использование возможностей языка запросов SQL — вот три столпа, на которых строится быстрая работа любой системы на платформе 1С.