При обработке XML-документов большого объема (от нескольких мегабайт и выше) разработчики часто сталкиваются с резким падением производительности встроенных механизмов 1С. Типичная ситуация: специализированные редакторы, такие как Oxygen XML, находят тысячи вхождений за доли секунды, в то время как метод ВычислитьВыражениеXPath в платформе 1С:Предприятие тратит на ту же задачу десятки секунд. Особенно остро проблема проявляется при использовании оси // (descendant-or-self).
В этой статье мы подробно разберем, почему стандартный поиск работает медленно, и рассмотрим эффективные способы оптимизации, которые позволяют ускорить выполнение запросов в 100 и более раз.
Выясним причину, по которой стандартное выражение типа /e/e[1]/e//d работает неэффективно. Реализация DOM в 1С базируется на библиотеке libxml2. Когда движок XPath встречает оператор //, он инициирует полный рекурсивный обход всех дочерних узлов от текущего уровня до самого низа дерева. На больших файлах с глубокой вложенностью это приводит к экспоненциальному росту количества проверок.
Проанализируем ситуацию: если ваша структура имеет фиксированную или ограниченную глубину, использование "динамической глубины" // заставляет систему выполнять избыточную работу, проверяя каждый узел на соответствие условию, вместо того чтобы целенаправленно переходить к нужным веткам.
Экспериментально доказано, что сумма поисков с фиксированной глубиной дает результат значительно быстрее, чем один поиск с использованием //. Рассмотрим оригинальный подход, который позволил сократить время поиска с 30 секунд до показателей, сопоставимых с профессиональным ПО.
Суть метода заключается в том, чтобы вместо одного универсального запроса выполнить серию запросов, постепенно увеличивая вложенность. Разберем по шагам реализацию этого алгоритма:
ШаблонУровня = "";
ф = 0;
// Предположим, максимальная глубина вложенности нам известна или ограничена (например, 8 уровней)
Для Счетчик = 0 По 8 Цикл
// Формируем выражение, заменяя // на конкретную цепочку уровней */
Выражение = "/e/e[1]/e/" + ШаблонУровня + "d[(position()=5 or position()=6) and starts-with(text(), '\"') and text()!='\"\"']/text()";
// Важно: создаем разыменователь один раз или используем существующий
Разыменователь = Новый РазыменовательПространствИменDOM(Новый Соответствие);
// Используем НеупорядоченныйИтераторУзлов для максимальной скорости
НаборУзлов = ДокументDOM.ВычислитьВыражениеXPath(Выражение, ДокументDOM, Разыменователь, ТипРезультатаDOMXPath.НеупорядоченныйИтераторУзлов);
Пока Истина Цикл
Узел = НаборУзлов.ПолучитьСледующий();
Если Узел = Неопределено Тогда
Прервать;
КонецЕсли;
// Обработка найденного узла
ф = ф + 1;
КонецЦикла;
// На каждой итерации добавляем один уровень вложенности
ШаблонУровня = ШаблонУровня + "*/";
КонецЦикла;
Важное примечание: Обратите внимание на использование ТипРезультатаDOMXPath.НеупорядоченныйИтераторУзлов. В отличие от возврата типа Строка или УпорядоченныйИтераторУзлов, этот метод работает быстрее, так как не требует от движка дополнительных затрат на сортировку узлов в порядке их появления в документе или преобразования типов данных.
Если производительность ДокументDOM все еще не удовлетворяет вашим требованиям, проанализируем альтернативный объект платформы — ЧтениеXML. Для файлов размером в несколько мегабайт и выше объектная модель DOM всегда будет потреблять больше памяти и работать медленнее, чем потоковое чтение.
Посмотрим на преимущества этого подхода:
В приведенном выше примере кода используется конструкция */, которая означает "любой узел на данном уровне". Если вам заранее известна структура XML (например, вы знаете, что после e идет row, а затем item), крайне рекомендуется заменить звездочки на конкретные имена тегов.
Проанализируем разницу: при использовании * движок XPath вынужден обращаться к таблице имен для каждого встреченного узла. Если же указано точное имя /row/item/d, парсер может отсекать целые ветки документа, не заходя внутрь узлов с другими именами. Это дает дополнительный прирост скорости на 15-20%.
Рассмотрим выражение фильтрации: starts-with(text(), '"') and text()!='""'. В XPath работа с текстовыми узлами через функцию text() может быть ресурсоемкой, если элемент содержит много под-узлов или комментариев.
Попробуем оптимизировать логику:
d нет вложенных элементов, а только текст, используйте проверку самого узла, а не его текстового содержимого, если это позволяет контекст.РазыменовательПространствИменDOM за пределы циклов. В исходном сообщении темы автор создавал его на каждой итерации, что создавало лишнюю нагрузку на выделение памяти и инициализацию объектов 1С.Подводя итог нашему анализу, можно выделить следующие ключевые правила для ускорения XPath в 1С:
// на больших документах. Заменяйте ее либо точным путем, либо циклом с постепенным увеличением вложенности */.НеупорядоченныйИтераторУзлов, если порядок следования элементов для вас не критичен.*.ЧтениеXML.Применение этих простых правил позволяет превратить медленный и тяжелый процесс обработки данных в эффективный инструмент, работающий за доли секунды.