В какой момент происходит выделение памяти для переменных, объявленных внутри функций?

Я разрабатываю сетевую игру, и для меня критически важна скорость вызова функции Update(). <br/><br/> Рассмотрим класс Unit (MonoBehavior), в котором реализована функция Move(). Из-за особенностей реализации эта функция вызывается в каждом кадре Update(). Движения в игре могут быть различными, и можно организовать их через параметры Move(int) или Move(string), где параметр задаёт направление (вперед, влево, поворот, прыжок, телепорт, КО). Использование семантических значений, таких как Move("Forward"), полезно для читаемости, но параметр int занимает меньше места и предпочтительнее - например, Move(1). Я хотел бы узнать: влияет ли размер переменных, используемых в функции, на скорость работы Update() при каждом его вызове? <br/> Другими словами, выделяется ли память для переменной внутри функции один раз (при запуске или компиляции), или она выделяется заново при каждом вызове функции? <br/><br/> Кроме того, у меня есть еще пара небольших вопросов: <br/> 1. Какова "тяжесть" проверки условия if(?) (включая проверку на null) в функции Update()? Я рассматриваю возможность создания двух наследников вместо использования if для оптимизации.
  • 10 сентября 2024 г. 12:14
Ответы на вопрос 2
1. **Выделение памяти для переменных:** 

В C# (и в Unity) локальные переменные, объявленные внутри функций, выделяют память каждый раз при каждом вызове функции. Это значит, что при каждом вызове метода `Move`, если вы объявляете переменную внутри этого метода, память для нее будет выделяться заново. Однако, примитивные типы (например, `int`, `float`) представляют собой относительно небольшие объекты, и их создание и уничтожение происходит быстро.

Для оптимизации в вашем случае возможно использование параметров методов так, чтобы минимизировать overhead. Например, если вы часто вызываете функцию с одним и тем же набором значений, вы можете рассмотреть вариант кэширования или создания перечисления (enum) вместо строк.

2. **Размер переменных и скорость вызова `Update()`:**

Да, размер переменных может влиять на производительность, хотя это зависит от контекста применения. Примитивные типы занимают меньше места и могут обрабатываться быстрее, чем строки (как `Move("Forward")`). Поэтому, если скорость критична, использование `Move(int)` будет более эффективным, чем использование строковых параметров.

Также стоит отметить, что использование строк может привести к дополнительным затратам на неявное создание объектов, так как строки в C# являются ссылочными типами.

3. **"Тяжесть" проверки условия `if`:**

Проверка условия `if`, даже с проверкой на `null`, обычно не приводит к значительным затратам по времени. Это простой оператор, который работает очень быстро. Тем не менее, если у вас есть ситуация, когда вы часто проверяете условия в каждом кадре (как в `Update()`), это может складываться в значительное время при большом количестве вызовов.

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

В общем, всегда хорошо проводить профилирование вашего кода. Unity имеет встроенные инструменты для профилирования, которые могут помочь измерить время выполнения ваших методов и определить узкие места.
Одно ядро AMD Ryzen 5000 выполняет до 20 инструкций за такт, в том числе IF(JPM/JNC),  если вы пишите игру для настольных систем, то такая оптимизация - это выкинутое на помойку ваше личное время, и время потраченное на другие аспекты игры (например тестирование, реиграбельность, начальная сложность и т.д.). 
Если ваш Move не вызывается 10000 раз за кадр, то вам не стоит вообще на него обращать внимание.
Внимание стоит обратить на графы Profiler, там скорей всего есть более насущные пожиратели производительности устройства, которые могут превосходить ваш Move() в миллионы раз по кол-ву обращений и емкости памяти.
Похожие вопросы