Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

The Profiler

Запускаешь игру в Godot и играешь. Игра увлекательна, она постепенно набирает функциональность, и кажется, что релиз уже близок.

Но затем вы открываете дерево навыков, и оно зависает из-за какой-то ошибки в коде. Смотреть, как дерево навыков прокручивается, словно это слайд-шоу, — это неприемлемо. Что пошло не так? С позиционированием элементов дерева навыков, с пользовательским интерфейсом или с рендерингом?

Можно попробовать всё оптимизировать и запускать игру снова и снова, но можно подойти к этому умнее и сузить круг возможностей. Воспользуйтесь профайлером Godot.

Обзор профилировщика

Вы можете открыть профилировщик, открыв панель Debugger и щелкнув вкладку Profiler.

../../../_images/profiler.png

Профилировщик Godot не запускается автоматически, поскольку профилирование требовательно к производительности. Он должен постоянно измерять всё происходящее в игре и сообщать результаты отладчику, поэтому по умолчанию он отключен.

Чтобы начать профилирование, запустите игру, а затем вернитесь в редактор. Нажмите кнопку Start в левом верхнем углу вкладки Profiler. Вы также можете установить флажок Autostart, чтобы профилировщик автоматически запускался при следующем запуске проекта. Обратите внимание, что состояние флажка Autostart не сохраняется между сеансами редактора.

Примечание

Профилировщик в настоящее время не поддерживает скрипты C#. Скрипты C# можно профилировать с помощью JetBrains Rider и JetBrains dotTrace с плагином поддержки Godot.

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

Измеренные данные

Интерфейс профилировщика разделён на две части: слева — список функций, справа — график производительности.

Основными измерениями являются время кадра, время выполнения физических задач, время простоя и время выполнения физических задач.

  • frame time — это время, которое требуется Godot для выполнения всей логики для всего изображения, от физики до рендеринга.

  • Physics frame — это время, которое Godot выделяет между обновлениями физики. В идеальном сценарии время кадра равно выбранному вами: 16,66 миллисекунды по умолчанию, что соответствует 60 FPS. Это система отсчёта, которую можно использовать для всего остального.

  • Idle time is the time Godot took to update logic other than physics, such as code that lives in _process() or timers and cameras set to update on Idle.

  • Physics time is the time Godot took to update physics tasks, like _physics_process() and built-in nodes set to update on Physics.

Примечание

Frame Time включает время рендеринга. Допустим, вы обнаружили в игре загадочный всплеск задержки, но при этом физика и скрипты работают быстро. Задержка может быть связана с появлением частиц или визуальных эффектов!

По умолчанию Godot устанавливает галочки для параметров Frame Time и Physics Time. Это позволяет оценить длительность каждого кадра относительно выделенного желаемого FPS для физики. Вы можете включать и выключать функции, устанавливая флажки слева. Другие функции появляются по мере продвижения по списку, например, Physics 2D, Physics, и Audio, прежде чем перейти к функциям скрипта, где отображается ваш код.

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

Область измерения и окна измерений

Вы можете изменить тип измерения, используя раскрывающееся меню Measure. По умолчанию оно начинается со значения Frame Time и отображает время, необходимое для прохождения кадра в миллисекундах. Среднее время — это среднее время, которое потребовалось любой функции при многократном вызове. Например, функция, выполнение которой заняло 0,05 миллисекунды пять раз, должна дать среднее время 0,01 миллисекунды.

Если точное количество миллисекунд не важно и вы хотите видеть соотношение времени к остальной части кадра, используйте процентные значения. Frame % рассчитывается относительно Frame Time, а Physics % — относительно Physics Time.

Последний параметр — это область действия времени. Включительно измеряет время, затраченное функцией с любыми вложенными вызовами функций. Например:

../../../_images/split_curve.png

get_neighbors, find_nearest_neighbor, and move_subject all took a lot of time. You could be fooled into thinking that this is because all three of them are slow.

Но если изменить на Self, Godot измеряет время, проведенное в теле функции, не принимая во внимание вызовы функций, которые он сам сделал.

../../../_images/self_curve.png

You can see that get_neighbors and move_subject have lost a lot of their importance. In effect, that means that get_neighbors and move_subject have spent more time waiting for some other function call to finish than not, and find_nearest_neighbor is actually slow.

Отладка медленного кода с помощью профайлера

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

В разделе Script functions установите флажки для некоторых функций, которые требуют времени. Именно эти функции необходимо проверить и оптимизировать.

Измерение вручную в микросекундах

If your function is complex, it could be challenging to figure out which part needs optimization. Is it your math or the way you access other pieces of data to do the math with? Is it the for loop? The if statements?

You can narrow down the measurement by manually counting ticks as the code runs with some temporary functions. The two functions are part of the Time class object. They are get_ticks_msec() and get_ticks_usec(). The first measures in milliseconds (1,000 per second), and the second measures in microseconds (1,000,000 per second).

Каждый из них возвращает количество времени, прошедшее с момента запуска игрового движка в соответствующем временном интервале.

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

# Measuring the time it takes for worker_function() to run
var start = Time.get_ticks_usec()
worker_function()
var end = Time.get_ticks_usec()
var worker_time = (end-start)/1000000.0

# Measuring the time spent running a calculation over each element of an array
start = Time.get_ticks_usec()
for calc in calculations:
    result = pow(2, calc.power) * calc.product
end = Time.get_ticks_usec()
var loop_time = (end-start)/1000000.0

print("Worker time: %s\nLoop time: %s" % [worker_time, loop_time])

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

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