Игрок пишет в чат: «пинг огромный, всё лагает». Админ смотрит на его задержку: 30 мс, красота. Игрок настаивает, что мир превратился в слайд-шоу. Правы оба, потому что говорят о разных часах. Пинг измеряет, сколько летит пакет между клиентом и сервером. Тик измеряет другое: сколько времени сервер тратит на один шаг симуляции мира. Можно иметь короткую дорогу и очень медленную кухню, и в этой статье мы будем разбираться с кухней.

Задача конкретная: вечером TPS проседает, днём всё нормально, и надо найти, на что именно уходит время сервера, а не покупать мощности наугад. Путь такой: превратить жалобы игроков в сцену, которую можно повторить, записать профиль spark в момент проблемы, прочитать его как распределение времени, изменить одну вещь и посмотреть на ту же сцену ещё раз.

Два разных «лага»: дорога и кухня

Начнём с терминов, потому что именно на них чаще всего ломается диагностика. По документации spark о TPS и MSPT, TPS — это число тиков в секунду, а MSPT — длительность одного тика в миллисекундах. В обычном режиме сервер стремится к 20 тикам в секунду, а значит, на каждый тик у него есть бюджет в 50 мс. Пока средняя обработка тика укладывается в этот бюджет, сервер успевает за собственными часами. Как только тик начинает регулярно занимать больше, шагов в секунду становится меньше 20, и весь мир замедляется: мобы ходят рывками, блоки ломаются с задержкой, предметы падают как в желе.

Обратите внимание, что MSPT — это не «время ответа игроку», а стоимость одного шага мира. Игроку всё равно, где именно тормозит: в сети или в симуляции. Но лечатся эти вещи совершенно по-разному. Если упёрлись в тик, то быстрый канал и хороший маршрут ничего не изменят, ведь пакеты будут приходить вовремя и приносить запоздавший мир. И наоборот: когда тик короткий, а у игрока плохой маршрут, оптимизация плагинов не поможет.

Вот поэтому в spark есть отдельные команды для разных сторон состояния. В справке по командам spark описано, что health показывает состояние ресурсов, tps — тики, ping — сетевую задержку игроков, а gc — сборку мусора. Четыре команды, четыре вопроса. Прежде чем запускать что-то тяжёлое, полезно задать их все и посмотреть, какой из ответов отличается от нормального.

Один нюанс про статистику, который сильно спасает от ложных выводов. В выдаче spark по тикам видны не только среднее, но и медиана, и 95-й процентиль. Это важно, потому что средняя длительность тика может выглядеть пристойно, а каждый двадцатый тик при этом оказывается тяжёлым. Игроки замечают именно такие провалы: не равномерную медленность, а подвисания. Если среднее в норме, а хвост распределения растёт, жалоба «лагает» всё равно справедлива, и искать надо события, которые случаются редко, но стоят дорого.

Переводим жалобу в сцену мира

«Вечером лагает» — это не наблюдение, а настроение. Для профилирования нужна сцена: где, что и сколько людей делает в момент провала. Профиль, снятый в пустой час, покажет честную картину пустого сервера, только к вечерней проблеме она отношения не имеет. В материале Paper о профилировании прямо сказано, что профиль полезен, когда исследуемая проблема воспроизводится во время записи. Мысль простая, но её легко нарушить: записали профиль «на всякий случай» в обед, увидели ровные цифры и решили, что виновата сеть.

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

  • В какой момент вечера начинается провал: когда набирается определённое число людей, после запланированного события, после перезапуска?
  • Где находятся игроки: все в одном месте, разбросаны по миру, летают на элитрах и открывают новые чанки?
  • Что они делают: строят, фармят на ферме, торгуют с жителями, устраивают бой?
  • Лагает у всех или у отдельных людей? Если у отдельных, это подозрение на сеть или клиент, и тогда нужна команда ping.

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

От фразы игрока к сцене, которую можно повторить
Что говорятКакая сцена за этим может стоятьЧто нужно зафиксировать
«Мобы тупят и замирают»Много существ в одной области: ферма, загон, деревняКоординаты места и приблизительное число существ вокруг
«Блоки возвращаются обратно»Тики не успевают, сервер отвечает с опозданиемПоказания tps в этот момент
«Рывки при беге по новой местности»Подгрузка и генерация чанков при исследовании мираСколько игроков одновременно уходят в новые территории
«Лагает только у меня»Проблема на стороне сети или клиента, а не тикаРезультат ping для этого игрока

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

Запишите сцену коротко, как протокол: время начала, число игроков онлайн, где собралась основная активность, какое событие предшествовало провалу. Эта запись понадобится дважды: для снятия профиля и для повторного сравнения после изменения. Без неё второй замер будет сравнением несравнимого, то есть вечера с вечером, в которых играли совсем по-разному.

Сначала быстрые вопросы: tps, ping, health, gc

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

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

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

Команда gc относится к сборке мусора. Её стоит посмотреть, чтобы понять, не связаны ли провалы с паузами памяти. Обратите внимание: есть соблазн сразу снять дамп кучи, раз уж подозрение пало на память. Документация spark отдельно предупреждает, что heap dump может занимать много места и содержать данные процесса. Это не первая команда на вечер, а крайняя мера: она создаёт большой файл на диске, и его нельзя выкладывать куда попало. Для нашей задачи, где подозреваем долгие тики, достаточно профиля.

Записываем профиль в момент проблемы

Теперь главное действие. У профилирования в spark есть два практических удобства, о которых стоит знать заранее, и оба описаны в справке по командам. Первое: параметр --timeout ограничивает длительность записи, так что профиль не остаётся включённым навсегда, если вы забыли про него в разгар вечера. Второе: параметр --only-ticks-over позволяет собирать только долгие тики. А команда остановки с параметром --save-to-file сохраняет результат в файл, не отправляя его на сайт.

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

spark profiler start --timeout <длительность> --only-ticks-over <порог>
spark profiler stop --save-to-file

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

Одно техническое условие, которое легко упустить. Paper включает spark начиная с ветки 1.21, об этом говорится в его документации по профилированию. Если у вас другое ядро или более старая версия, наличие профайлера нужно проверять отдельно: возможно, spark придётся ставить как отдельный плагин, а возможно, он уже подключён иначе. Не стоит предполагать, что команда есть везде.

И ещё одно практическое соображение. Профилирование само требует ресурсов: сбор данных добавляет работы серверу, и на машине, которая и так на пределе, запись профиля может чуть ухудшить картину. Поэтому ограничение по времени — не формальность, а способ не мучить сервер дольше, чем нужно. Для диагностики вам нужна короткая, но точная запись проблемного окна, а не марафон.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Профиль — это распределение времени, а не список виновных

Теперь файл у вас в руках, и начинается самая тонкая часть. Самая частая ошибка новичка: открыть профиль, найти самую толстую строку и объявить её преступником. Такая логика привлекательна, но неверна, и вот почему.

Профиль отвечает на вопрос «куда ушло время», а не «кто напортачил». Он показывает дерево вызовов, где у каждой ветки есть доля общего времени. Если какая-то ветка занимает большую часть тиков, это значит лишь, что программа проводит там много времени. А уж почему — выяснить придётся самому. Возможно, она делает много работы, потому что мира много. Возможно, потому что её вызывают слишком часто. Возможно, потому что в ней действительно что-то неэффективное. Профиль различает эти случаи только с вашей помощью.

Представьте разницу на бытовом примере. Если в бухгалтерии выяснилось, что половина бюджета ушла на аренду, это не значит, что арендодатель мошенничает. Аренда может быть дорогой, потому что офис большой. Вопрос не «кто виноват», а «нужен ли нам такой офис». Так же и с долей в профиле: большая доля — это повод задать вопрос, а не готовый приговор.

Вот на что стоит смотреть при чтении:

  • Главный поток тиков. Нас интересует то, что делает основной поток сервера во время тика, потому что именно он определяет, уложился ли тик в 50 мс.
  • Крупные ветви верхнего уровня. Какие большие категории работы делят время: сущности, чанки, блочные сущности, задачи плагинов, сетевые пакеты. Не лезьте сразу в мелкие листья: сначала поймите расклад между крупными частями.
  • Привязка к плагинам. Профиль помогает увидеть, какие плагины появляются в тяжёлых ветвях. Но присутствие плагина в ветви означает, что его код вызывается, а не что он плохой. Плагин может честно делать то, о чём его попросили, на слишком большом объёме данных.
  • Форма, а не одна строка. Одна доминирующая ветвь и десять равных по весу — это принципиально разные картины. В первом случае есть рычаг, во втором простой оптимизации не найти.

Условный пример чтения

Чтобы показать ход мысли, возьмём выдуманные числа; к реальному серверу они отношения не имеют. Допустим, в отфильтрованном профиле долгих тиков одна крупная ветвь занимает около половины времени и относится к обработке существ, ещё заметная часть уходит на задачи одного из плагинов, а остальное разложено мелко. Что отсюда следует?

Во-первых, есть кандидат на изменение: обработка существ. Но «существа» — ещё не диагноз. Надо вернуться к сцене. Если по протоколу вечерняя нагрузка связана с игроками, которые собираются у большой фермы, то гипотеза «слишком много существ в одной области» согласуется и с профилем, и с наблюдением. Два независимых источника указывают в одну сторону, и это уже серьёзнее, чем один.

Во-вторых, вторая ветвь, плагин, — отдельная гипотеза, и её не надо смешивать с первой. Если одновременно крутить и существ, и плагин, то после улучшения вы не узнаете, что именно помогло. Поэтому в нашем примере выбираем один рычаг, самый крупный и самый согласованный со сценой. Второй остаётся в записной книжке до следующего круга.

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

Меняем одну вещь

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

Как выбрать изменение? Три критерия, которые стоит применить по порядку:

  1. Крупность. Рычаг должен иметь заметную долю в профиле. Оптимизация ветви в два процента не изменит ощущения игроков, как бы красиво она ни выглядела.
  2. Согласованность со сценой. Ветвь профиля должна объясняться тем, что игроки реально делали. Если профиль указывает на подсистему, к которой у сцены нет отношения, стоит усомниться: возможно, вы записали не то окно или причина лежит глубже.
  3. Обратимость. Предпочитайте изменение, которое легко откатить. Если ограничить число существ у фермы не понравится игрокам, вы вернёте всё как было и будете знать, что именно эту цену платил сервер.

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

Теперь запишите, что именно сделали и когда. Одна строка в заметках: «изменил такой-то параметр с такого значения на такое, во столько-то». Звучит занудно, но именно такие записи отличают диагностику от камлания.

Сравниваем ту же сцену

Изменение сделано. Теперь надо проверить, что оно сработало, а проверка — это снова профиль и снова сцена. Здесь полезна дисциплина «как в прошлый раз»: то же время вечера, примерно такое же число игроков, то же место активности, те же параметры записи. Если в прошлый раз у фермы собралась компания, а сейчас все разбежались по миру, сравнение бессмысленно: вы будете сравнивать ферму с пустым полем и объявите победу, которой не было.

Что именно сравнивать? Не одно число, а несколько:

  • MSPT: среднее, медиана и 95-й процентиль. Если улучшилось среднее, но хвост остался тяжёлым, вы убрали часть проблемы, но подвисания никуда не делись. Хвост распределения — это то, что игроки чувствуют острее всего.
  • Доля прежней ветви в профиле. Если вы воздействовали на существ, уменьшилась ли их доля среди долгих тиков? Это прямое доказательство, что рычаг тот самый.
  • Количество долгих тиков. Если фильтр по долгим тикам теперь собирает меньше событий при той же длительности записи, сервер реже опаздывает.
  • Слова игроков. Субъективные ощущения — тоже данные, хотя и самые шумные. Они хорошо дополняют цифры, но не заменяют их.

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

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

Когда рычаг найден, но этого мало

Бывает и так, что профиль честно указал на дорогую операцию, которую нельзя убрать. Ферма нужна сообществу, игроки не хотят отказываться от больших построек, а плагин даёт функцию, без которой сервер теряет смысл. Тогда вопрос ресурсов становится законным. Но он уже звучит иначе: не «возьмём побольше ядер, вдруг поможет», а «мы знаем, какая операция мира стоит дорого, и хотим проверить, как её переносит другая конфигурация».

Именно в такой постановке выбор тарифа превращается в эксперимент, а не в лотерею. В публичном предложении VPS MAATRIX есть линейка планов с разным сочетанием ресурсов: Ba — 1 vCPU, 2 ГБ RAM и 40 ГБ NVMe; Uab — 2 vCPU, 4 ГБ и 80 ГБ; Heka — 4 vCPU, 8 ГБ и 160 ГБ; Maat — 8 vCPU, 16 ГБ и 240 ГБ; Djed — 16 vCPU, 32 ГБ и 320 ГБ. Серверы построены на AMD EPYC и NVMe, а доступ к машине даётся с root, так что вы сами ставите ядро, spark и всё остальное. Эти цифры — отправная точка для теста, а не обещание, что определённое число игроков поместится на определённом плане: ёмкость покажет только ваша сцена на вашем мире.

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

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

Вечер, который можно объяснить

Вечерний провал TPS редко бывает таинственным. Обычно за ним стоит конкретное событие: люди собрались в одном месте, запустили одну и ту же дорогую операцию, и тик перестал укладываться в свои 50 мс. Задача профилирования в том, чтобы назвать эту операцию по имени и доле времени, а не гадать между сетью, процессором и плагинами.

Порядок действий при этом короткий. Сначала отделить ping от тика, потому что дорога и кухня лечатся разными способами. Затем описать сцену, в которой проблема повторяется. Записать профиль в это окно, ограничив его по времени и при необходимости оставив только долгие тики. Прочитать результат как распределение времени и свериться с тем, что делали игроки. Изменить одну вещь, самую крупную и самую согласованную со сценой. И снять тот же профиль в той же ситуации, чтобы сравнивать сравнимое.

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