Операция закрытия месяца, а именно процедура корректировки стоимости, часто становится «бутылочным горлышком» в высоконагруженных системах на базе 1С (особенно в конфигурациях 1С:УПП 1.3 и 1С:КА 1.1). Ситуации, когда расчет себестоимости длится более 5 часов, создают серьезные простои для бизнеса. В этой статье мы детально разберем, как проанализировать замеры производительности и какие изменения в программном коде позволяют сократить время выполнения этой операции в 5 раз и более.
Рассмотрим ситуацию, когда замер производительности указывает на модуль ОбщийМодуль.КорректировкаСтоимости.Модуль, а именно на процедуру РассчитатьСписаниеПоСредней. Основная проблема часто кроется в неэффективном алгоритме поиска состояний товара. В типовом коде часто встречается конструкция «Для Каждого Элемент Из Соответствие», где внутри цикла происходит сравнение свойств структур. Если количество перемещений товара велико, система вынуждена выполнять миллиарды операций сравнения, что приводит к квадратичной сложности алгоритма.
Проанализируем фрагмент проблемного кода, где поиск выполняется полным перебором:
Для Каждого Строка Из Таб Цикл
// поиск выполняется полным перебором
НайденоСостояние=Ложь;
Для Каждого ЭлементСостояние Из СоотвПараметровСостояний Цикл
НайденоСостояние=Истина;
Для Каждого Элемент Из СтруктураСостояния Цикл
НайденоСостояние = НайденоСостояние И (ЭлементСостояние.Значение[Элемент.Ключ] = Строка[Элемент.Ключ]);
КонецЦикла;
Если НайденоСостояние Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЦикла;
В данном примере СоотвПараметровСостояний — это объект типа Соответствие, но поиск по нему идет не по ключу (что было бы быстро), а путем перебора всех его элементов. Это сводит на нет все преимущества использования хеш-таблиц.
Для оптимизации этого узкого места мы заменим перебор в Соответствие на поиск в объекте ТаблицаЗначений с использованием встроенных индексов платформы 1С. Это позволит системе использовать быстрый поиск вместо последовательного сканирования.
Разберем по шагам, как изменить код:
ТаблицаПоиска.Индексы.Добавить().НайтиСтроки().Рассмотрим оптимизированный пример кода:
// Создаем таблицу для быстрого поиска
ТаблицаПоиска = Новый ТаблицаЗначений;
СтрокаПолейПоиска = "";
// Наполняем колонки исходя из структуры состояния
Для Каждого Колонка Из Таб.Колонки Цикл
// ... логика определения колонок состояния ...
ТаблицаПоиска.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
СтрокаПолейПоиска = СтрокаПолейПоиска + ?(ЗначениеЗаполнено(СтрокаПолейПоиска), ",", "") + Колонка.Имя;
КонецЦикла;
ТаблицаПоиска.Колонки.Добавить("Индекс");
// Критически важный момент: добавляем индекс по полям поиска
ТаблицаПоиска.Индексы.Добавить(СтрокаПолейПоиска);
Для Каждого Строка Из Таб Цикл
СтруктураПоиска = Новый Структура(СтрокаПолейПоиска);
ЗаполнитьЗначенияСвойств(СтруктураПоиска, Строка);
// Вместо вложенного цикла используем быстрый поиск по индексу
НайденныеСтроки = ТаблицаПоиска.НайтиСтроки(СтруктураПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
ИндексСостояния = НайденныеСтроки[0].Индекс;
Иначе
ИндексСостояния = ТаблицаПоиска.Количество();
НовСтр = ТаблицаПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НовСтр, СтруктураПоиска);
НовСтр.Индекс = ИндексСостояния;
КонецЕсли;
Строка.Источник = ИндексСостояния;
КонецЦикла;
Такой подход позволяет сократить время выполнения данного блока с нескольких часов до нескольких минут, так как поиск в индексированной таблице работает значительно быстрее, чем полный перебор элементов Соответствие.
После устранения первой проблемы замер производительности часто смещается на следующий блок — поиск и сворачивание встречных перемещений (например, когда товар перемещался со Склада А на Склад Б и обратно в рамках одного периода). В типовом коде это также реализовано через вложенный цикл, что дает сложность $O(N^2)$.
Выясним причину замедления: для каждой строки таблицы система ищет в этой же таблице «зеркальную» строку (где источник равен приемнику текущей строки, а приемник — источнику). Для оптимизации мы можем применить метод «Хеширования».
Вместо вложенного цикла Пока Инд2 < КолВо Цикл мы рекомендуем использовать временное Соответствие, где ключом будет выступать комбинация Источник + "_" + Приемник. Это позволит найти встречное движение за одну операцию поиска.
Пример логики для ускорения этого блока:
ХешПеремещений = Новый Соответствие;
// Сначала заполняем хеш-карту всех перемещений
Для Каждого Строка Из Таб Цикл
Ключ = Строка.Источник + "_" + Строка.Приемник;
ХешПеремещений.Вставить(Ключ, Строка);
КонецЦикла;
// Затем ищем встречные одним проходом
Для Каждого Строка Из Таб Цикл
ОбратныйКлюч = Строка.Приемник + "_" + Строка.Источник;
Строка2 = ХешПеремещений[ОбратныйКлюч];
Если Строка2 <> Неопределено Тогда
// Выполняем взаимозачет количеств и стоимостей
// ...
КонецЕсли;
КонецЦикла;
Если оптимизация кода выполнена, но закрытие месяца всё еще занимает значительное время, проанализируем влияние серверной части и СУБД. Процесс корректировки стоимости генерирует огромную нагрузку на временные таблицы (TempDB).
Рассмотрим важные рекомендации по настройке железа и SQL-сервера:
TempDB должны располагаться на максимально быстрых SSD/NVMe дисках. Это критично, так как 1С активно использует МенеджерВременныхТаблиц при расчете себестоимости.Max Degree of Parallelism равным 1. Это предотвращает избыточные затраты ресурсов на параллелизацию сложных запросов, которые могут только замедлять расчет.Управляемые блокировки. В режиме автоматических блокировок (особенно на старых конфигурациях) корректировка стоимости может блокировать таблицы на чтение, вызывая конфликты и ожидание.Проанализируем состояние самой информационной базы. Скорость корректировки напрямую зависит от чистоты данных:
1. Отрицательные остатки: Если в системе есть списания «в минус», алгоритм корректировки стоимости может работать некорректно или совершать лишние итерации по «развязыванию» стоимости. Предварительное исправление отрицательных остатков значительно ускоряет закрытие месяца.
2. Зацикленные перемещения: Встречаются ситуации, когда документы создают петли: Склад 1 -> Склад 2 -> Склад 1. Алгоритм пытается исключить такой «холостой ход», и чем больше таких петель, тем сложнее расчет графа перемещений. Рекомендуется минимизировать такие операции в рамках одного месяца.
3. Свертка характеристик: Если в учете используются сотни характеристик, которые фактически не влияют на расчет стоимости, стоит рассмотреть возможность их упрощения или настройки разрезов учета, так как каждая аналитика добавляет размерности в таблицу перемещений.
Подводя итог, можно сказать, что основное ускорение (в 5 раз, как в рассмотренном нами случае) достигается за счет перевода алгоритмов с линейного поиска O(N) на поиск по индексам O(log N) или хеш-таблицам O(1). Совместное использование программной оптимизации и правильной настройки SQL-сервера позволяет превратить мучительное многочасовое ожидание в быстрый и предсказуемый процесс.