Производительность фильтров

В MapLibre GL JS фильтры слоёв представляют собой механизм условного отбора геометрии из источника данных перед её отображением. Каждый фильтр — это выражение, которое вычисляется для каждой фичи в слое и определяет, будет ли она включена в рендеринг. На уровне архитектуры это операция, тесно связанная с фазой подготовки данных к отрисовке в WebGL-пайплайне.

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


Модель вычисления фильтров

Фильтр слоя интерпретируется как дерево выражений. Каждый узел дерева — это оператор (логический, сравнения, проверки наличия свойств), а листья — значения свойств фичи или константы.

Основной принцип выполнения:

  • для каждой фичи источника выполняется обход дерева выражения
  • результат вычисления приводит к булевому значению
  • фича либо попадает в пайплайн слоя, либо исключается

При большом количестве фичей (десятки и сотни тысяч) стоимость вычисления становится линейной относительно числа объектов и сложности выражения.


Стоимость различных типов фильтров

Простые предикаты

Наиболее дешёвыми являются операции прямого сравнения:

  • равенство (==)
  • неравенство (!=)
  • сравнение чисел (>, <, >=, <=)
  • проверка наличия свойства (has)

Такие операции обычно компилируются в простые инструкции и хорошо оптимизируются движком.


Логические композиции

Фильтры с all, any, none создают дополнительную ветвистость вычисления:

  • all может завершаться досрочно при первом false
  • any — при первом true
  • none — инверсия any

Однако при глубокой вложенности возникает рост количества операций и ухудшение предсказуемости исполнения на уровне JS-движка.


Сложные выражения

Наиболее дорогими являются:

  • регулярные выражения
  • вычисляемые строки (concat, downcase, upcase)
  • арифметика над свойствами
  • многоуровневые вложенные условия

Регулярные выражения особенно критичны, так как их стоимость растёт нелинейно при увеличении длины строки и количества проверяемых фич.


Узкие места при большом количестве данных

Пересчёт при каждом событии камеры

Любое изменение состояния карты (pan, zoom, rotate) может инициировать повторную оценку фильтров. При этом:

  • геометрия остаётся неизменной
  • но пересчитывается видимость фичей
  • происходит повторная сборка слоёв для рендера

На больших наборах данных это становится доминирующим фактором CPU-нагрузки.


Влияние источников данных

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

GeoJSON-источники, напротив, требуют полной обработки всего набора данных при каждом обновлении.


Стратегии оптимизации фильтров

Уменьшение пространства проверки

Эффективная оптимизация начинается с сокращения числа фичей до применения фильтра:

  • предварительная агрегация данных на сервере
  • разбиение источников по категориям
  • использование нескольких слоёв вместо одного сложного

Чем меньше входной набор, тем ниже стоимость вычисления фильтра.


Использование minzoom и maxzoom

Фильтры, зависящие от масштаба, часто заменяются комбинацией:

  • minzoom
  • maxzoom

Это позволяет исключить целые классы фичей на уровне слоя до начала вычисления выражений.

Такой подход особенно эффективен при визуализации плотных наборов точек.


Избегание вычисляемых строк

Операции преобразования строк внутри фильтра существенно увеличивают стоимость:

  • downcase и upcase требуют обработки каждой строки
  • concat создаёт новые временные значения
  • частые операции над строками приводят к лишним аллокациям

Более эффективный подход — хранение уже нормализованных значений в свойствах данных.


Снижение глубины выражений

Глубокие вложенные конструкции увеличивают:

  • число вызовов интерпретатора выражений
  • нагрузку на стек выполнения
  • сложность оптимизации

Плоские фильтры с минимальной вложенностью работают заметно быстрее.


Индексация данных как альтернатива сложным фильтрам

Фильтрация в стиле часто заменяет собой индексирование, которое отсутствует в рантайме.

Более производительный подход:

  • заранее группировать фичи по атрибутам
  • выносить категорию в отдельный слой
  • использовать разные источники для разных классов объектов

Например, вместо фильтра по типу:

  • один слой для дорог
  • один слой для зданий
  • один слой для водных объектов

Это устраняет необходимость проверки == "road" для каждой фичи.


Динамические фильтры и их стоимость

Изменение фильтра во время выполнения приводит к:

  • пересборке слоя
  • повторной оценке всех фичей
  • сбросу части кэша рендеринга

Частые обновления фильтров (например, при интерактивных UI-элементах) создают постоянную нагрузку на main thread.

Оптимизация достигается через:

  • дебаунс обновлений
  • хранение состояния вне фильтра
  • минимизацию числа уникальных комбинаций фильтров

Влияние фильтров на GPU-пайплайн

Хотя сами фильтры выполняются на CPU, их результат влияет на GPU следующим образом:

  • уменьшается или увеличивается количество отрисовываемых вершин
  • меняется размер батчей рендеринга
  • влияет на эффективность инстансинга

Слишком сложные фильтры могут привести к фрагментации батчей, снижая эффективность отрисовки WebGL.


Практика проектирования эффективных фильтров

Приоритет простых сравнений

Фильтры вида:

  • равенство по числовому ID
  • диапазоны значений
  • булевые флаги

обладают минимальной стоимостью и хорошо масштабируются.


Избегание вычислений в фильтре

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

  • на этапе подготовки GeoJSON
  • в ETL-процессе
  • в тайловом сервере

Разделение логики по слоям

Чем меньше условных ветвлений внутри одного слоя, тем стабильнее производительность. Разделение слоёв позволяет:

  • уменьшить количество проверок на фичу
  • повысить кэшируемость результатов
  • снизить нагрузку при обновлениях

Масштабирование и большие наборы данных

При работе с сотнями тысяч и миллионами объектов ключевым фактором становится не только сложность фильтра, но и:

  • количество слоёв с фильтрами
  • частота пересчёта стиля
  • структура источников данных

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