Мониторинг производительности

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

Основные показатели:

  • FPS (frames per second) — частота кадров
  • CPU load — нагрузка на главный поток
  • GPU time — время отрисовки кадра
  • Tile load time — время загрузки тайлов
  • Style parse time — время применения стиля
  • Memory usage — потребление памяти WebGL и JS

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


Встроенные события MapLibre GL JS для мониторинга

Библиотека предоставляет набор событий, позволяющих отслеживать жизненный цикл рендеринга и загрузки данных.

События загрузки тайлов

map.on('dataloading', (e) => {
    console.log('Начата загрузка данных:', e);
});

map.on('data', (e) => {
    console.log('Изменение состояния данных:', e);
});

map.on('idle', () => {
    console.log('Карта в состоянии покоя');
});

Событие idle особенно важно: оно сигнализирует, что карта завершила все текущие задачи рендеринга и загрузки. Его используют как базовый индикатор стабильности производительности.


Использование performance API браузера

MapLibre GL JS не изолирован от браузерного окружения, поэтому стандартный Performance API является основным инструментом глубокого анализа.

Измерение времени рендера кадра

let lastTime = performance.now();

function measureFrame() {
    const now = performance.now();
    const delta = now - lastTime;
    lastTime = now;

    const fps = 1000 / delta;

    console.log('FPS:', fps.toFixed(2));

    requestAnimationFrame(measureFrame);
}

measureFrame();

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


Отслеживание загрузки тайлов

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

map.on('tileloadstart', (e) => {
    console.log('Старт загрузки тайла', e.tile);
});

map.on('tileload', (e) => {
    console.log('Тайл загружен', e.tile);
});

map.on('tileerror', (e) => {
    console.log('Ошибка тайла', e.tile, e.error);
});

Для производственного мониторинга обычно собираются следующие данные:

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

Анализ WebGL контекста

MapLibre GL JS использует WebGL, поэтому производительность зависит от состояния GPU-контекста.

Контроль потери контекста

map.on('webglcontextlost', (e) => {
    console.warn('WebGL контекст потерян');
    e.preventDefault();
});

map.on('webglcontextrestored', () => {
    console.log('WebGL контекст восстановлен');
});

Потеря контекста часто связана с:

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

Профилирование рендеринга через map.repaint

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

map.repaint = true;

Однако более практичным является косвенный анализ через события render:

map.on('render', () => {
    console.log('Кадр отрисован');
});

Частота вызова render помогает определить, насколько активно работает сцена.


Замер времени выполнения стиля

Сложные стили с выражениями (expressions) могут существенно замедлять отрисовку.

Пример анализа времени применения стиля:

const start = performance.now();

map.setStyle('style.json');

map.on('styledata', () => {
    const end = performance.now();
    console.log('Время загрузки стиля:', end - start);
});

Особенно чувствительны:

  • вложенные case и match
  • фильтры с множественными условиями
  • динамические выражения по zoom

Мониторинг загрузки источников данных

Источники (sources) могут быть векторными, растровыми или GeoJSON. Их загрузка критична для общей производительности.

map.on('sourcedataloading', (e) => {
    console.log('Загрузка источника:', e.sourceId);
});

map.on('sourcedata', (e) => {
    console.log('Обновление источника:', e.sourceId);
});

Для оптимизации важно отслеживать:

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

Анализ памяти

JavaScript-окружение и WebGL используют разные области памяти. Контроль утечек особенно важен при длительной работе карты.

setInterval(() => {
    if (performance.memory) {
        console.log('JS Heap:', performance.memory.usedJSHeapSize);
        console.log('Total Heap:', performance.memory.totalJSHeapSize);
    }
}, 5000);

Основные причины роста памяти:

  • накопление неиспользуемых источников
  • частое создание/удаление слоёв
  • большое количество растровых тайлов
  • неочищенные обработчики событий

Инструментирование через requestAnimationFrame

Глубокий анализ производительности рендера требует отслеживания каждого кадра.

function loop() {
    const start = performance.now();

    requestAnimationFrame(() => {
        const end = performance.now();
        const renderTime = end - start;

        console.log('Render time:', renderTime);
        loop();
    });
}

loop();

Этот метод позволяет выявлять:

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

Диагностика перегруженных слоёв

Слои (layers) являются основным источником деградации производительности.

Характерные признаки перегрузки:

  • резкое падение FPS при zoom
  • задержки при pan
  • длительная перерисовка label-слоёв
const layers = map.getStyle().layers;

console.log('Количество слоёв:', layers.length);

Рекомендовано анализировать:

  • количество SymbolLayer
  • использование text-field
  • наличие кластеризации
  • частоту обновления источников

Использование DevTools и WebGL инспекции

Chrome DevTools предоставляет инструменты для анализа WebGL-контекста:

  • вкладка Performance — анализ кадров
  • вкладка Memory — утечки памяти
  • вкладка Rendering — FPS meter
  • вкладка WebGL — трассировка draw calls

MapLibre GL JS генерирует большое количество draw calls при сложных стилях, поэтому анализ GPU становится критическим.


Сбор метрик в реальном времени

Для продакшн-систем часто используется централизованный сбор телеметрии.

function sendMetrics(data) {
    fetch('/metrics', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: { 'Content-Type': 'application/json' }
    });
}

map.on('render', () => {
    sendMetrics({
        fps: calculateFPS(),
        tiles: map.style.sourceCaches,
        timestamp: Date.now()
    });
});

Ключевые метрики для отправки:

  • FPS
  • количество активных тайлов
  • время загрузки источников
  • количество слоёв
  • состояние idle/render

Оптимизация на основе собранных данных

Собранные метрики позволяют выявлять системные узкие места:

  • снижение количества слоёв уменьшает GPU нагрузку
  • упрощение выражений ускоряет style evaluation
  • кластеризация GeoJSON снижает количество точек
  • использование vector tiles уменьшает сетевую нагрузку

Мониторинг производительности в MapLibre GL JS является непрерывным процессом, встроенным в архитектуру приложения, а не разовой диагностикой.