Profiling и debugging

Производительность MapLibre GL JS напрямую зависит от взаимодействия JavaScript-логики, WebGL-рендера, загрузки тайлов и компоновки слоёв. Узкие места чаще всего возникают не в одном компоненте, а на стыке нескольких подсистем: рендеринг кадра, декодирование вектора тайлов, стиль-вычисления и работа с источниками данных.

Ключевой метрикой становится стабильность FPS и время одного кадра (frame time). При нормальной работе карта удерживает 60 FPS, что соответствует ~16.6 ms на кадр. Превышение этого значения приводит к микрофризам и деградации интерактивности.

Измерение производительности через Performance API

Базовый инструмент — Performance API, позволяющий фиксировать пользовательские метки:

performance.mark('map-render-start');

map.once('render', () => {
  performance.mark('map-render-end');
  performance.measure(
    'map-render',
    'map-render-start',
    'map-render-end'
  );

  const measures = performance.getEntriesByName('map-render');
  console.log(measures);
});

Цикл рендеринга MapLibre включает множество кадров, поэтому более точный подход — подписка на событие render и накопление статистики:

let frameTimes = [];

map.on('render', () => {
  const now = performance.now();
  frameTimes.push(now);

  if (frameTimes.length > 60) {
    frameTimes.shift();
  }
});

На основе массива временных меток вычисляется средний FPS и вариативность кадра.

Использование событий жизненного цикла карты

MapLibre GL JS предоставляет набор событий, отражающих состояние рендера:

  • load — завершение загрузки стиля
  • idle — отсутствие активных операций
  • render — каждый кадр
  • data — загрузка или обновление данных
  • dataloading — начало загрузки источника

Комбинация render и idle позволяет оценивать реальную стоимость обновлений:

map.on('idle', () => {
  console.log('карта стабилизировалась');
});

Частое отсутствие состояния idle указывает на бесконечные перерисовки, часто вызванные анимациями или постоянно изменяющимися источниками данных.

Инспекция тайлов и источников данных

Производительность сильно зависит от поведения векторных тайлов. Для анализа используется:

const features = map.querySourceFeatures('source-id');
console.log(features);

Дополнительно полезен метод:

map.getSource('source-id').tileCacheDebug;

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

Типичные проблемы:

  • избыточное количество слоёв в стиле
  • слишком крупные GeoJSON-источники вместо векторных тайлов
  • отсутствие фильтрации на уровне источника
  • частая инвалидизация источника через setData

Debug-флаги рендерера

MapLibre GL JS наследует набор визуальных отладочных режимов, полезных для диагностики рендеринга:

map.showTileBoundaries = true;
map.showCollisionBoxes = true;
map.repaint = true;

Эти режимы позволяют визуализировать:

  • границы тайлов
  • коллизии подписей
  • зоны перерисовки

Отдельное значение имеет анализ overdraw — наложения пикселей при рендеринге слоёв. При перегруженных стилях возникает избыточное заполнение фрагментного шейдера, что резко снижает FPS.

Анализ WebGL через DevTools

Chrome DevTools предоставляет вкладку Performance и Rendering, где важны следующие инструменты:

  • Frame Rendering Stats (FPS meter)
  • Paint flashing
  • Layer borders
  • WebGL context tracking

Дополнительно можно отслеживать WebGL контекст:

const canvas = map.getCanvas();
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');

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

  • утечки GPU памяти
  • потеря контекста (webglcontextlost)
  • деградация драйвера

Обработка событий:

canvas.addEventListener('webglcontextlost', (e) => {
  console.warn('WebGL context lost');
  e.preventDefault();
});

Профилирование загрузки тайлов

Network-панель используется для анализа:

  • времени ответа tile server
  • размера vector tiles (.pbf)
  • кэширования HTTP

Проблемные паттерны:

  • большое количество мелких тайлов
  • отсутствие gzip/brotli сжатия
  • высокая задержка первого байта (TTFB)

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

  • minzoom / maxzoom
  • генерацией предагрегированных тайлов
  • уменьшением детализации геометрии

Анализ стиля и слоёв

Сложность стиля прямо влияет на FPS. Каждый слой может содержать:

  • фильтры (filter)
  • вычисляемые выражения (interpolate, match)
  • динамические свойства (paint, layout)

Особенно затратны:

  • layout с текстовыми метками
  • символы с collision detection
  • частые изменения paint свойств

Инструментальная проверка:

console.log(map.getStyle().layers);

Глубокие стили требуют анализа количества draw calls, которые косвенно влияют на GPU нагрузку.

queryRenderedFeatures и диагностика визуальных ошибок

Метод используется для проверки того, какие объекты реально участвуют в рендеринге:

map.queryRenderedFeatures([x, y], {
  layers: ['layer-id']
});

Типовые сценарии:

  • несоответствие данных и визуализации
  • фильтры, исключающие нужные объекты
  • проблемы z-index слоёв

Отдельно полезен радиусный запрос:

map.queryRenderedFeatures(
  [[x1, y1], [x2, y2]],
  { layers: ['layer-id'] }
);

Отслеживание перерисовок и “лишних” render циклов

Частая проблема — постоянный re-render без изменения данных. Причины:

  • вызовы setPaintProperty в цикле
  • анимации через requestAnimationFrame
  • обновление источников с одинаковыми данными
  • hover-эффекты без оптимизации состояния

Пример диагностики:

let renderCount = 0;

map.on('render', () => {
  renderCount++;
  if (renderCount % 60 === 0) {
    console.log('renders:', renderCount);
  }
});

Если карта не переходит в idle, это прямой сигнал постоянной нагрузки.

Инструментирование памяти и утечек

Основные источники утечек:

  • неотписанные event listeners
  • создание новых источников без removeSource
  • накопление GeoJSON данных
  • сохранение ссылок на map instance

Проверка памяти через DevTools:

  • Heap Snapshot
  • Allocation instrumentation

Типовой паттерн роста памяти:

  • добавление маркеров без удаления
  • постоянное обновление GeoJSON через setData

Web Worker и влияние на производительность

MapLibre GL JS активно использует Web Worker для:

  • парсинга векторных тайлов
  • вычисления геометрии
  • подготовки данных для WebGL

Задержки возникают при:

  • больших tile payload
  • частой пересборке источников
  • перегрузке CPU при декодировании

Диагностика:

  • вкладка Performance → Main thread vs Worker thread
  • анализ “Scripting” времени

Комплексный цикл оптимизации

Типовой цикл анализа включает:

  • фиксацию FPS и frame time
  • проверку событий render / idle
  • анализ Network тайлов
  • проверку WebGL load
  • инспекцию слоёв и style complexity
  • проверку memory heap

На уровне практики ключевым индикатором становится несоответствие между:

  • количеством draw calls
  • размером загруженных тайлов
  • временем выполнения layout/paint операций