Skip to content

Использование Spark для диагностики производительности сервера

Spark — это мощный инструмент для профилирования производительности серверов Minecraft. Он помогает выявлять причины лагов, анализировать использование ресурсов и понимать, как работает ваш сервер. Эта статья проведет вас через основные концепции и команды Spark.

Понимание серверного тика (Игрового Цикла)

Почти все видеоигры (включая Minecraft) управляются одним большим циклом программы. Выполнение сервера (и игры) разбито на «тики».

Каждый раз, когда происходит «тик», игровой сервер выполняет ряд действий, включая:

  • Обработку входящих пакетов от игроков (например, движение, установка/разрушение блоков, атака других сущностей).
  • Обновление положения игроков и других сущностей.
  • Отправку исходящих пакетов всем игрокам о событиях на сервере (изменения блоков, движение и действия сущностей).
  • Спавн мобов, обработку ИИ мобов, поиск пути и т.д.
  • Обработку обновлений редстоуна.
  • И многое другое!

Тики в Minecraft

Сервер Minecraft стремится выполнять ровно 20 таких игровых «тиков» каждую секунду, или, другими словами, один тик каждые 50 миллисекунд.

Линейное представление тиков

Конечно, объем работы, требуемый в каждом тике, варьируется в зависимости от того, что происходит в игре, поэтому тики на практике никогда не будут такими регулярными!

Когда сервер работает нормально

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

Если время тика меньше 50 мс, сервер будет «спать» оставшееся время, пока не будет готов к выполнению следующего тика. Например:

  • Сервер тратит 15 миллисекунд на выполнение тика.
  • Контроллер цикла тиков «спит» (ничего не делает) в течение 35 миллисекунд, прежде чем начать выполнение следующего тика.

Тики с периодами сна

Как видно, все тики в этом примере заняли менее 50 миллисекунд, но они были распределены так, чтобы ровно 20 тиков обрабатывались в секунду.

Когда сервер лагает

Если тик занимает более 50 миллисекунд, выполнение следующего тика задерживается, так как тики не могут выполняться параллельно. Когда это происходит, игровой процесс начинает ухудшаться, так как все становится менее отзывчивым и «лагающим».

Лагающие тики

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

Из этого примера также видно, откуда берутся метрики TPS (тиков в секунду) и MSPT (миллисекунд на тик).

Основные метрики: TPS и MSPT

Команда /spark tps показывает TPS (тиков в секунду) и MSPT (миллисекунд на тик) сервера. Но что означают эти значения?

Эти две метрики являются основным способом оценки производительности сервера. Они относятся к Игровому Циклу: сколько «тиков» происходит и сколько времени они занимают в среднем.

TPS (тиков в секунду)

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

Для нормальной, безлаговой игры «магическое число» — 20 тиков в секунду.

Чтобы достичь этого, в среднем каждый тик должен длиться 50 миллисекунд или меньше.

Разбор лагающих тиков (влияние на TPS)

В этом примере некоторые тики заняли более 50 миллисекунд, и только 16 тиков были завершены в течение секунды. Если бы это продолжалось в среднем, spark сообщил бы, что сервер работает с 16 TPS.

MSPT (миллисекунд на тик)

Метрика MSPT (миллисекунд на тик) показывает, сколько миллисекунд в среднем игра тратит на обработку каждого тика.

Для нормальной, безлаговой игры «магическое число» — 50 или меньше миллисекунд на тик.

Разбор лагающих тиков (влияние на MSPT)

Используя тот же пример, можно увидеть, как варьируется продолжительность каждого тика.

  • «Самый быстрый» тик занял всего 20 миллисекунд, поэтому spark сообщит это как «min MSPT».
  • «Самый медленный» тик занял 100 миллисекунд, поэтому spark сообщит это как «max MSPT».

Spark также вычисляет другие значения на основе этих данных: медианное значение и 95-й процентиль.

Вывод команды /spark tps

Вот что означают цифры в команде /spark tps 😎

Пример вывода команды /spark tps

Spark автоматически подсвечивает значения зеленым, желтым или красным цветом в зависимости от того, насколько хорошо/плохо они отражают производительность сервера.

Диагностика лаг-спайков

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

Это может происходить довольно часто, например, 1 из каждых 20 тиков, или редко, например, раз в минуту. Обычно они связаны с поведением игроков.

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

К счастью, у spark есть два полезных инструмента для решения этой проблемы.

Шаг 1: Используйте /spark tickmonitor для обнаружения лаг-спайка

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

Мы можем использовать команду /spark tickmonitor для этого.

Эта команда работает следующим образом: сначала устанавливает среднюю частоту тиков сервера, а затем:

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

Чтобы включить мониторинг, просто выполните /spark tickmonitor.

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

Вы также можете указать порог как абсолютную продолжительность тика, например, /spark tickmonitor --threshold-tick 50, чтобы сообщать о любом тике, который превышает 50 миллисекунд (это точка, в которой сервер должен начать догонять или лагать).

Пример работы /spark tickmonitor

Затем вам просто нужно подождать. Если спайки, которые вы испытываете, заметны в игровом процессе, постарайтесь сопоставить эффекты спайка в игре с выводом мониторинга.

Если вывод недостаточно чувствителен, попробуйте более низкий порог, например, /spark tickmonitor --threshold-tick 70. (В оригинальном примере, чтобы вызвать спайк, использовался WorldEdit).

Демонстрация /spark tickmonitor с WorldEdit

Как видно, тики во время выполнения действия WorldEdit показали увеличение более чем на 1000%!

Шаг 2: Используйте /spark profiler с опцией --only-ticks-over для поиска причины

Опция --only-ticks-over означает, что spark будет профилировать только те игровые тики, которые длятся дольше указанного порога. Это отфильтровывает все «нормальные» игровые процессы и оставляет только лагающие тики.

Вы можете использовать Шаг 1, чтобы определить подходящее значение порога. Рекомендуется использовать значение от 50 до 100, но оно всегда должно быть меньше продолжительности «лагающих» тиков.

Например, лагающие действия, выявленные в примере на шаге 1, длились более 300 миллисекунд, но чтобы быть уверенным, что они будут включены, используем более низкий порог в 150 миллисекунд.

Затем выполните, например, /spark profiler --only-ticks-over 150.

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

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

Например: Пример профиля с --only-ticks-over

Тики в профилях Spark (Интерпретация результатов)

Вы можете четко определить цикл тиков в профилях Spark, он обычно виден почти в самом верху!

Тики в профилировщике Spark

В приведенном выше примере мы видим, что waitForNextTick() (ожидание следующего тика) составляет 81% активности ЦП в потоке «Server thread». Это нормально! Это означает, что в среднем сервер мог потратить около 80% из 50 миллисекунд, выделенных на каждый тик, ничего не делая (ожидая).

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

В общем:

  • Более высокий процент «сна» (waitForNextTick()) — это хорошо.
  • Если у вас менее 20% «сна» (и, следовательно, более 80% на «тик»), ваш сервер работает довольно усердно и может лагать на некоторых тиках (помните, что эти значения являются средними!).
  • Если у вас менее 5% «сна» (и, следовательно, более 95% на «тик»), ваш сервер, вероятно, лагает и не имеет запасной мощности.

Важно помнить

Эти значения являются средними

Гипотетически, ваш сервер может тратить всего 20 миллисекунд на обработку большинства тиков (нормально), но затем иногда тратить 300 миллисекунд на обработку некоторых тиков (ненормально!). Вероятно, это происходит, если вы испытываете «лаг-спайки», а не общую низкую производительность.

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

Команды Spark

Важно

/sparkb, /sparkv и /sparkc должны использоваться вместо /spark на BungeeCord, Velocity и Forge/Fabric клиентских установках соответственно.

Профилировщик

/spark profiler

Управляет профилировщиком Spark.

  • Разрешение: spark или spark.profiler

Если профилировщик уже запущен:

  • /spark profiler open: Открыть страницу просмотра профилировщика без его остановки.
  • /spark profiler stop: Остановить профилировщик и просмотреть результаты.
    • --comment <комментарий>: Включить указанный комментарий в просмотрщик.
    • --save-to-file: Сохранить профиль в файл в каталоге конфигурации вместо загрузки.
  • /spark profiler cancel: Отменить профилировщик и остановить его без загрузки результатов.

Основное использование:

  • /spark profiler start: Запустить профилировщик в режиме по умолчанию (профилирование CPU основного потока сервера).
    • --timeout <секунды>: Запустить профилировщик и автоматически остановить его через указанное количество секунд.
    • --thread *: Запустить профилировщик и отслеживать все потоки.
    • --alloc: Запустить профилировщик и профилировать выделение памяти (давление на память) вместо использования CPU.
      • --alloc-live-only: Запустить профилировщик выделения памяти, сохраняя статистику только для объектов, которые не были собраны сборщиком мусора к концу профилирования.
      • --interval <байты> (для --alloc): Запустить профилировщик выделения памяти и производить выборку с указанной частотой в байтах (по умолчанию 524287, т.е. 512 КБ).
  • /spark profiler stop: Остановить профилировщик и просмотреть результаты (см. выше для опций с --comment и --save-to-file).
  • /spark profiler info: Проверить текущий статус профилировщика.

Расширенные аргументы для /spark profiler start (Профилировщик выполнения):

  • --interval <миллисекунды>: Запустить профилировщик и производить выборку с указанным интервалом (по умолчанию 4 мс).
  • --thread <имя потока>: Запустить профилировщик и отслеживать только указанные потоки.
  • --thread <шаблон имени потока> --regex: Запустить профилировщик и отслеживать только потоки, соответствующие указанному регулярному выражению.
  • --only-ticks-over <миллисекунды>: Запустить профилировщик, но записывать выборки только из тиков, которые длятся дольше указанной продолжительности. (Полезно для диагностики лаг-спайков, как описано выше).
  • --combine-all: Запустить профилировщик, но объединить все потоки под одним корневым узлом.
  • --not-combined: Запустить профилировщик, но отключить группировку потоков из пула потоков.
  • --ignore-sleeping: Запустить профилировщик, но записывать выборки только из потоков, которые не находятся в состоянии 'sleeping'.
  • --force-java-sampler: Запустить профилировщик и принудительно использовать Java сэмплер вместо асинхронного.

Состояние сервера

/spark health

Генерирует отчет о состоянии сервера, включая TPS, использование CPU, памяти и диска.

  • Разрешение: spark или spark.healthreport
  • Опции:
    • /spark health --upload: Загрузить отчет о состоянии в просмотрщик spark и получить ссылку для обмена.
    • /spark health --memory: Включить дополнительную информацию об использовании памяти JVM.
    • /spark health --network: Включить дополнительную информацию об использовании сети системой.

/spark ping

Отображает информацию о среднем (или конкретном) времени пинга игроков.

  • Разрешение: spark или spark.ping
  • Использование:
    • /spark ping: Показать информацию о среднем пинге всех игроков.
    • /spark ping --player <имя_игрока>: Показать текущий пинг конкретного игрока.

/spark tps

Отображает информацию о TPS (тиках в секунду) сервера и использовании CPU. Подробное объяснение метрик смотрите выше.

  • Разрешение: spark или spark.tps

/spark tickmonitor

Управляет системой мониторинга тиков. Полезно для обнаружения лаг-спайков, как описано выше.

  • Разрешение: spark или spark.tickmonitor
  • Использование:
    • /spark tickmonitor: Включить/выключить систему мониторинга.
    • /spark tickmonitor --threshold <процент>: Запустить мониторинг тиков, сообщая только о тиках, которые превышают процентное увеличение от средней продолжительности тика.
    • /spark tickmonitor --threshold-tick <миллисекунды>: Запустить мониторинг тиков, сообщая только о тиках, которые превышают указанную продолжительность в миллисекундах.
    • /spark tickmonitor --without-gc: Запустить мониторинг тиков и отключить отчеты о деятельности сборщика мусора.

Память

/spark gc

Отображает информацию об истории сборки мусора (GC) на сервере.

  • Разрешение: spark или spark.gc

/spark gcmonitor

Управляет системой мониторинга сборки мусора (GC).

  • Разрешение: spark или spark.gcmonitor
  • Использование:
    • /spark gcmonitor: Включить/выключить систему мониторинга.

/spark heapsummary

Генерирует новый дамп сводки памяти (кучи) и загружает его в просмотрщик.

  • Разрешение: spark или spark.heapsummary
  • Опции:
    • /spark heapsummary --run-gc-before: Предложить JVM запустить сборщик мусора перед генерацией сводки кучи. (устарело)

/spark heapdump

Генерирует новый файл дампа кучи (.hprof снимок) и сохраняет его на диск.

  • Разрешение: spark или spark.heapdump
  • Опции:
    • /spark heapdump --compress <тип>: Указать, что дамп кучи должен быть сжат с использованием указанного типа. Поддерживаемые типы: gzip, xz и lzma.
    • /spark heapdump --include-non-live: Указать, что "неживые" объекты (объекты, которые недостижимы и подлежат сборке мусора) должны быть включены. (устарело)
    • /spark heapdump --run-gc-before: Предложить JVM запустить сборщик мусора перед генерацией дампа кучи. (устарело)

Прочее

/spark activity

Отображает информацию о недавней активности, выполненной spark.

  • Разрешение: spark или spark.activity
  • Опции:
    • /spark activity --page <номер_страницы>: Показать указанную страницу.