Performance тесты

При работе с Mapbox GL JS производительность нельзя свести к одному числу. Система рендеринга построена на WebGL, использует асинхронную загрузку тайлов, worker-потоки для парсинга данных и сложный пайплайн от источника данных до пикселей на экране. Поэтому тестирование опирается на набор взаимосвязанных метрик.

Ключевые показатели:

  • FPS (frames per second) — стабильность отрисовки сцены при взаимодействии
  • Time to First Render (TTFR) — время до первого отображения карты
  • Time to Fully Loaded Map — момент, когда все тайлы и стили загружены
  • Tile Load Time — задержка загрузки и декодирования векторных тайлов
  • Memory usage (JS heap + GPU memory) — потребление памяти браузером
  • CPU usage — нагрузка на main thread и workers
  • Interaction latency — задержка реакции на pan/zoom/rotate
  • Shader compile time — стоимость компиляции WebGL программ

Каждая метрика должна рассматриваться в контексте сценария: статическая карта, интерактивная навигация, анимации или плотные дата-визуализации.


Архитектура, влияющая на тестирование

Производительность Mapbox GL JS определяется несколькими слоями:

  • Main thread

    • обработка событий пользователя
    • управление состоянием карты
    • orchestration рендеринга
  • Worker threads

    • парсинг vector tiles (PBF)
    • генерация геометрии
    • подготовка данных для WebGL
  • GPU pipeline

    • рендеринг слоёв
    • выполнение шейдеров
    • композиция кадров

Особенность тестирования в том, что узкое место может находиться в любом из этих уровней, и классические JS-бенчмарки недостаточны.


Базовая методология performance-тестов

Типовой подход к тестированию включает три уровня:

1. Synthetic benchmark (искусственные сцены)

Используются контролируемые данные:

  • фиксированное число источников (sources)
  • заранее заданное количество слоёв (layers)
  • одинаковый набор тайлов
  • отключение сетевых вариаций через кеш

Цель — сравнение конфигураций без внешнего шума.


2. Real-world dataset testing

Используются реальные стили и данные:

  • городские карты с высокой плотностью POI
  • спутниковые слои + векторная подложка
  • сложные выражения фильтров (filter expressions)
  • большое количество symbol layers

Цель — оценка поведения в реальных условиях.


3. Stress testing

Сценарии экстремальной нагрузки:

  • десятки тысяч точек
  • агрессивный clustering
  • множественные animated layers
  • частые camera updates (flyTo, easeTo)
  • параллельная загрузка стилей

Измерение FPS и стабильности рендеринга

FPS в WebGL-картах — не просто среднее значение, а распределение кадров.

Важно измерять:

  • mean FPS
  • 1% low FPS (плохие кадры)
  • frame jitter (дисперсия времени кадра)

Пример базового измерения:

let lastTime = performance.now();
let frames = 0;
let fpsValues = [];

function loop() {
  const now = performance.now();
  frames++;

  if (now - lastTime >= 1000) {
    fpsValues.push(frames);
    frames = 0;
    lastTime = now;
  }

  requestAnimationFrame(loop);
}

loop();

При анализе Mapbox GL JS важно учитывать, что падение FPS часто связано не с рендерингом, а с:

  • пересчётом layout symbol layers
  • перекомпоновкой источников данных
  • GC-паузами

Time to First Render (TTFR)

TTFR зависит от цепочки:

  1. загрузка JS-библиотеки
  2. создание WebGL контекста
  3. загрузка стиля
  4. загрузка sprite и glyphs
  5. первичная отрисовка тайлов

Особенно критичны:

  • размер style.json
  • количество sources
  • наличие custom fonts
  • сетевые задержки тайлов

Инструментально измеряется через performance.mark:

performance.mark('map-start');

map.on('load', () => {
  performance.mark('map-loaded');
  performance.measure('map-ttfr', 'map-start', 'map-loaded');
});

Производительность тайловой системы

Vector tiles — центральный элемент архитектуры.

Факторы влияния:

  • размер тайла (gzip / pbf compression)
  • количество геометрий
  • сложность атрибутов
  • zoom level density
  • caching behavior

Узкие места:

  • декодирование PBF в worker
  • triangulation для fill layers
  • simplification geometry
  • sorting features by layer order

GPU и шейдерная нагрузка

WebGL-пайплайн Mapbox GL JS зависит от:

  • количества draw calls
  • сложности fragment shaders
  • blending операций
  • overdraw

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

  • слишком много symbol layers → увеличение draw calls
  • большие fill-extrusion сцены → высокая нагрузка на fragment shader
  • прозрачность слоёв → overdraw

Сложность стилей и слоёв

Количество слоёв напрямую влияет на производительность.

Критические факторы:

  • symbol layers — самые дорогие
  • layout properties — пересчитываются при каждом zoom
  • paint properties — влияют на GPU
  • expressions — могут вычисляться на CPU

Особенно тяжелы:

"filter": ["all",
  ["has", "population"],
  [">", ["get", "population"], 100000]
]

При увеличении количества фильтров стоимость рендера растёт нелинейно.


Camera updates и интерактивность

Операции:

  • pan
  • zoom
  • rotate
  • flyTo

влияют на:

  • пересчёт матрицы камеры
  • перерасчёт видимых тайлов
  • перерисовку слоёв

Тестирование включает:

  • continuous zoom stress
  • rapid pan simulation
  • inertia testing

Memory leak тестирование

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

  • неосвобождённые sources
  • accumulation event listeners
  • cached tiles
  • persistent WebGL buffers

Методика:

  • длительный прогон (30–120 минут)
  • фиксированный сценарий движения карты
  • периодический snapshot heap

Инструменты профилирования

Используются:

  • Chrome DevTools Performance tab
  • WebGL inspector
  • Mapbox telemetry hooks
  • performance.getEntries()
  • memory snapshots

Типовой анализ:

  • flame chart main thread
  • worker activity breakdown
  • GPU frame timing (через external tools)

Бенчмарки реальных сценариев

Городская карта

  • 50k–200k POI
  • clustering enabled
  • 10–15 layers

Проблема:

  • symbol layout bottleneck

Heatmap

  • большие datasets
  • aggregation on GPU

Проблема:

  • fragment shader overload

3D extrusion

  • fill-extrusion layers
  • high zoom levels

Проблема:

  • fill rate GPU bottleneck

Оптимизационные наблюдения, выявляемые тестами

  • уменьшение symbol layers даёт наибольший прирост FPS
  • clustering снижает CPU, но увеличивает preprocessing cost
  • tile caching критичен для smooth pan
  • simplification geometry улучшает TTFR
  • снижение precision координат ускоряет render pipeline

Автоматизация performance regression testing

Типовой pipeline:

  • фиксированный dataset
  • headless Chromium
  • puppeteer сценарии
  • сбор FPS / memory / TTFR
  • сравнение с baseline

Пример структуры теста:

await page.goto('http://localhost:3000');

const metrics = await page.evaluate(() => {
  return window.__MAP_METRICS__;
});

Интерпретация результатов

Важный аспект — не абсолютные значения, а деградация:

  • +10% draw time при добавлении слоя
  • рост memory usage после zoom cycles
  • ухудшение 1% low FPS при clustering off

Показатели должны анализироваться в динамике между версиями и конфигурациями стиля.