Быстро ли работает функция Выполнить() в 1С при частых вызовах?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции нестандартного торгового, складского или промышленного оборудования разработчики часто сталкиваются с задачей: протоколы обмена постоянно меняются и пополняются, а обновлять конфигурацию ради небольших правок парсера неудобно. Возникает идея хранить алгоритмы обработки входящих данных прямо в справочнике или текстовых файлах и вызывать их с помощью функции Выполнить(). Проанализируем, как устроен этот механизм, насколько он уступает встроенному коду по скорости и какие альтернативы позволяют избежать постоянных обновлений конфигурации.

Механизм работы Выполнить() и причины падения производительности

Главное отличие кода, написанного в общих модулях или модулях форм, от динамических строк в Выполнить() заключается в предварительной компиляции. Штатные модули конфигурации компилируются платформой заранее при старте сеанса или первом обращении к контексту. Скомпилированный байт-код хранится в оперативной памяти и исполняется виртуальной машиной 1С без повторной подготовки.

С динамическим кодом ситуация принципиально иная. Платформа 1С:Предприятие не кэширует байт-код для произвольных строк, переданных в метод Выполнить(). Каждый раз, когда поток выполнения доходит до этой команды, платформа последовательно выполняет:

  1. Лексический и синтаксический анализ переданного текста.
  2. Трансляцию инструкций во внутренний псевдокод (байт-код платформы).
  3. Связывание контекста видимости переменных.
  4. Непосредственное выполнение скомпилированного блока.

Если код вызывается разово (например, при проведении одного документа вручную или по нажатию сервисной кнопки), время компиляции составляет доли миллисекунды, и пользователь визуально не заметит никакой задержки. Однако при потоковой обработке данных от оборудования, когда порции байтов приходят несколько раз в секунду, постоянная перекомпиляция создает существенную нагрузку на процессор и приводит к заметным задержкам интерфейса.

Особенности замеров и влияние режима отладки

При попытке замерить скорость через конфигуратор можно столкнуться с парадоксальными результатами, когда динамическое выполнение Вычислить() или Выполнить() в замерах кажется сопоставимым по скорости с обычными вызовами. Разберем, почему так происходит.

Включение режима отладки вносит серьезные искажения в работу виртуальной машины 1С. Штатные механизмы вызова методов обрастают отладочными перехватами и служебным трафиком между клиентом/сервером и конфигуратором. Чтобы получить объективную картину быстродействия, все тесты производительности необходимо выполнять исключительно в рабочем режиме без подключенной отладки, фиксируя время с помощью функции ТекущаяУниверсальнаяДатаВМиллисекундах() на больших сериях итераций (например, от 10 000 до 100 000 вызовов подряд).

Кэширование данных и попытки ускорения через контекст

В качестве компромисса можно попробовать минимизировать операции внутри динамического фрагмента, заранее подгружая статические справочные данные в контекст вызывающей процедуры. Посмотрим на пример изоляции предварительных настроек:


Перем МоеПространствоИмен Экспорт;

Функция ВыполнитьПарсинг(ТекстАлгоритма, ВходящийПакет)
    Если МоеПространствоИмен = Неопределено Тогда
        // Загружаем тяжелые таблицы коэффициентов и сопоставлений один раз
        МоеПространствоИмен = ИнициализироватьТаблицыСоответствия();
    КонецЕсли;
    
    // В динамический код передается уже подготовленный контекст
    Выполнить(ТекстАлгоритма);
КонецФункции

Такой подход избавляет от повторного чтения настроек из базы данных, но не решает фундаментальную проблему: сам текст ТекстАлгоритма все равно будет компилироваться заново при каждом входящем событии.

Риски и ограничения использования динамического кода

Помимо просадки производительности, использование Выполнить() несет в себе архитектурные и платформенные ограничения:

  1. Безопасный режим: Если алгоритм вызывается в контексте с установленным признаком БезопасныйРежим (актуально для механизмов БСП, разделенных баз и внешних подключаемых модулей), работа с Выполнить() жестко блокируется платформой.
  2. Сложность поиска ошибок: В случае возникновения исключения функция ИнформацияОбОшибке() не покажет реальную строку в исходном файле, возвращая стек вида {(1)}, что крайне затрудняет расследование сбоев на работающей системе.
  3. Конфликт областей видимости: Код внутри Выполнить() разделяет контекст с процедурой, из которой вызван. Случайно созданная внутри строка переменная с именем, совпадающим с переменной вызывающей процедуры, молча перезапишет ее значение.

Рекомендуемые альтернативы без изменения основной конфигурации

Если задача состоит в том, чтобы оперативно обновлять алгоритмы взаимодействия с оборудованием без необходимости блокировать пользователей монопольными обновлениями конфигурации, рассмотрим проверенные на практике варианты:

  1. Использование внешних обработок (EPF):

    Обработку с модулем парсера можно хранить в двоичных данных справочника базы. При старте сеанса или открытии формы обработки мы загружаем внешнюю обработку один раз через ВнешниеОбработки.Создать(). В этот момент модуль обработки компилируется единожды. Далее при поступлении каждого события оборудования вызываются экспортные функции полученного объекта напрямую, со скоростью нативного кода платформы.

  2. Расширения конфигурации (.cfe):

    Вынесение протоколов обмена в расширение конфигурации позволяет применять доработки динамически, не снимая конфигурацию поставщика с поддержки и не прерывая работу пользователей структурными изменениями метаданных.

  3. Внешние компоненты (Native API):

    Разбор нестандартных протоколов часто требует побитовых сдвигов, расчета контрольных сумм (CRC16, CRC32) и работы с сырыми байтовыми массивами. Перенос этого узкого места в компактную внешнюю библиотеку на C++ или Rust полностью устраняет вычислительную нагрузку с платформы 1С и обеспечивает мгновенную реакцию интерфейса на поступающие пакеты.

← На главную