Однажды появилась задача ускорить печать из 1С в документы MS Office Word. Формирование документов происходит около 60-90 секунд.
Программы: «1С:Зарплата и управление персоналом, редакция 3.1.14.98», MS Office Word 2016.
Много ранее я работал с печатью в MS Word, и проблем с производительностью не возникало, но и шаблон был всего один, и заполнение каждого параметра было прописано кодом, и БСП не использовалось.
В данном же случае: внешняя обработка печатает 3 шаблона, вызывается функция БСП для каждого шаблона; соответственно, параметры заполняются в функции БСП.
Я запустил замер производительности конфигуратором, чтобы посмотреть, какие именно строки занимают более всего времени.
Результат замера
В топе находятся строки кода, которые работают с шаблоном MS Word через COM-соединение. Суммарно первые строки занимают время: 20+14+12+6+6+6 = 64 секунды — это почти 60% времени. Если удастся сократить это время хотя бы на половину, но эффект уже будет заметен.
Разбираем каждую строку.
Строка №1.
Первая строка (Object.Select();) выполняется 219 раз — это достаточно много. Возникает вопрос: почему так много раз она выполняется? Может, в шаблонах так много параметров? Суммарно в шаблонах оказалось 95 параметров. Смотрим стек вызовов и находим причину столь частого выполнения кода.
На уровень раньше видим цикл.
Значит, в переменную ДанныеОбъекта передается излишне больше параметров, чем на самом деле необходимо.
Смотрим, откуда передается столько параметров, т.е. еще на уровень раньше.
Переменная Данные передана, поэтому переходим еще на уровень раньше.
Снова передача переменной Данные, поэтому снова переходим на уровень раньше.
Это процедура ПечатьМакета. В данном случае то, что передавалось в предыдущих переменных Данные — это значение переменной лПараметры (тип Соответствие), которое заполняется запросом, но заполняется всевозможными данными, нужными и ненужными, после чего вызывается процедура БСП.
Первая неоптимальность найдена — передача в процедуру БСП заранее лишнего количества параметров. Решить данную ситуацию можно просто: передать только необходимое количество параметров, которые используются в шаблоне.
Далее если вернуться еще на уровень раньше, то видим самое начало — процедура Печать.
Процедура Печать выполняет печать трех шаблонов, иными словами, три раза вызывает одну и ту же процедуру ПечатьМакета, в которой одна из неоптимальностей уже найдена.
Решено так: отдельная процедура заполняет только нужные параметры, ничего лишнего.
Немного про особенность замены.
На сайтах предлагается следующий вариант замены:
Процедура ЗаменитьНестабильно(знач Object, Параметр, Значение, ДополнитьПараметрОтличСимволами = Истина)
СтрокаПоиска = ?(ДополнитьПараметрОтличСимволами, "{v8 " + Параметр + "}", Параметр);
СтрокаЗамены = Строка(Значение);
Попытка
Object.Find.Execute(СтрокаПоиска,,,,,,,,,Значение, 2);
Исключение
ОпОш = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("ЗаменаПарамтровWord", УровеньЖурналаРегистрации.Ошибка, , ОпОш, ОпОш);
КонецПопытки;
КонецПроцедуры
Однако этот способ показал свою нестабильность следующим образом:
Ошибка при вызове метода контекста (Execute): Произошла исключительная ситуация (0x80020005)
И более никакой информации, никаких подробностей; в интернете искать можно долго и безрезультатно; исследовать можно тоже долго и безрезультатно.
Поэтому замена выполнена типовой процедурой.
Строка №2.
Перейдем ко второй топовой времязатратной строке замера:
Handler.НастройкиСтраницыМакета.Вставить(ИмяНастройки, COMОбъект.ActiveDocument.PageSetup[ИмяНастройки]);
Смотрим, в каком именно месте она вызывается.
Вызывается эта строка 69 раз в типовой функции ПолучитьМакетMSWord, получающей макет MSWord. Так как шаблон MS Word сохранен в макете с уже заранее настроенными параметрами, то в данном случае вызываемая строка просто лишняя. Исключая данную строку, функционал остается прежним и сокращается время формирования на 14 секунд.
Строка №3.
Теперь рассмотрим третью времязатратную строку:
COMОбъект = Новый COMОбъект("Word.Application");
Смотрим, в каком именно месте она вызывается.
Объект COM создается три раза в той же типовой функции ПолучитьМакетMSWord.
Однако COM объект MS Word способен работать с несколькими документами MS Word, поэтому в данном случае создаются лишние COM объекты, на которые затрачивается время.
Решение: создание только одного COM объекта, с помощью которого заполняются все три шаблона. Решена эта ситуация кешированием COM соединения следующим образом.
Строка №4.
Четвертая строка аналогична третьей:
COMОбъект = Новый COMОбъект("Word.Application");
Но вызывается данная строка в типовой функции ИнициализироватьПечатнуюФормуMSWord тоже три раза.
Возникает вопрос: если в типовой функции ПолучитьМакетMSWord уже создан COM объект, то почему же вызывается типовая функция ИнициализироватьПечатнуюФормуMSWord, в которой тоже создается COM объект?
По стеку вызовов находим процедуру, в которой вызывается функция ИнициализироватьПечатнуюФормуMSWord, и попадаем во внешнюю обработку.
По коду видно, похоже, была попытка обойтись средствами БСП, пренебрегая производительностью, так как вызваны две функции, создающие COM объект, и обе функции в цикле.
В данном случае функцию ИнициализироватьПечатнуюФормуMSWord можно исключить, используя только переменную Макет.
Строка №5.
Рассмотрим пятую времязатратную строку.
Поиск = COMСоединение.Selection.Find;
Строка вызывается три раза в типовой функции ПолучитьПозициюНачалаОбласти перед непосредственным поиском и заменой.
Так как достаточно использовать типовую процедуру Заменить, которая выполняет замену параметров по всему документу, то функцию ПолучитьПозициюНачалаОбласти можно исключить.
После оптимизации кода получаем следующий замер.
Результат оптимизации — всего 14-16 секунд вместо 90.
Во вложении к публикации обработки до и после оптимизации.
Выводы
- Нельзя сразу использовать функции БСП. Это влияет на производительность. Нужно смотреть функцию БСП и предусматривать влияние при ее использовании.
- Функции БСП содержат код, который может быть лишним при решении конкретной задачи, в результате лишний код влияет на производительность.