Экономия на работе однотипных скриптов
Добавлено:
28 май 2017, 21:06
Inessa
В ходе профилирования своей игры я обнаружила следующий момент.
Много вычислительных ресурсов зря тратится на выполнение однотипных скриптов, прикрепленных к однотипным объектам.У меня по уровню вдоль гоночной трассы программно генерируются и расставляются игровые объекты типа Coins - вращающиеся объекты в виде монеты.
Пользователь во время езды касается монетки, монетка исчезает и попадает в копилку игрока.
Вращающиеся монетки Coins сделаны в виде префаба, который программно инстанцируется вдоль трасы в различных ее местах.
К этому префабу подключен скрипт-ротатор, вращающий монетку вокруг своей оси.
В профилировщике видно, что когда на трассе вращается только одна монетка, то нагрузки от работы ее скрипта в профилировщике вообще не видно (все другие скрипты в тестовой сцене отключены).
Когда на сцене начинается вращаться 500 монеток, то в профилировщике это уже становится заметно и начинает падать FPS.
Следовательно, необходимо найти какой-то способ сэкономить на выполнении этой однотипной операции вращения одной и той же монетки.
Вот скрипт который я использую для этих целей.
Подскажите, пожалуйста, что нужно сделать, чтобы как-то сэкономить ресурсы на вращении монет?Т.е. чтобы Unity обсчитывал вращение для одной монетки, а применял это вращение для всех остальных монеток?
Используется csharp
using UnityEngine;
using System.Collections;
public class PickUpRotator : MonoBehaviour
{
void Update()
{
transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
}
}
Re: Экономия на работе однотипных скриптов
Добавлено:
28 май 2017, 21:28
samana
Inessa писал(а):Т.е. чтобы Unity обсчитывал вращение для одной монетки, а применял это вращение для всех остальных монеток?
Вы сами ответили на свой вопрос.
Все монетки должны находится в массиве. В Update один раз рассчитываете вращение, а потом в цикле перебираете все монетки и устанавливаете их поворот таким же.
Re: Экономия на работе однотипных скриптов
Добавлено:
28 май 2017, 21:45
seaman
Зачем иметь сразу все монетки? У Вас весь уровень с 500 монетами на экране виден?
Re: Экономия на работе однотипных скриптов
Добавлено:
28 май 2017, 22:06
Cr0c
Сделал на одном скрипте все монетки - 500 штук грузят на 13%-15% процессор
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class coin_test : MonoBehaviour {
public GameObject prefab_coin;
[Range(10,1000)]
public int coin_count = 10;
[Range(0f, 10f)]
public float speed = 1f;
[SerializeField]
public List<Transform> trs = new List<Transform>();
public float dist = 10f;
// Use this for initialization
void Start () {
Vector3 up_left = Camera.main.ScreenToWorldPoint(new Vector3(0f, Screen.height, dist));
Vector3 zero = Camera.main.ScreenToWorldPoint(new Vector3(0f, 0f, dist));
Vector3 down_right = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0f, dist));
Vector3 hor = down_right - zero;
Vector3 ver = up_left - zero;
for (int i=0; i<coin_count; i++)
{
float rx = Random.Range(0f, 1f);
float ry = Random.Range(0f, 1f);
Vector3 pos = zero + hor * rx + ver * ry;
Transform tr = (Instantiate(prefab_coin, pos, Quaternion.identity)).transform;
trs.Add(tr);
}
}
// Update is called once per frame
void Update () {
//foreach (Transform tr in trs)
for (int i=0; i<trs.Count; i++)
trs[i].Rotate(Vector3.up, speed, Space.World);
}
}
P.S.:
В Update один раз рассчитываете вращение, а потом в цикле перебираете все монетки и устанавливаете их поворот таким же.
это уменьшает нагрузку примерно на 1%-1.5% по профайлеру
Re: Экономия на работе однотипных скриптов
Добавлено:
29 май 2017, 00:03
Inessa
Скажите, а какие существуют метрики, чтобы я могла корректно сравнивать проделанные тесты?
Ведь задача оценки выигрыша от проделанной оптимизации многопараметрическая.
В моем исходном варианте на каждой инстанцированной монетке висел свой скрипт, который сам вращал ее без привлечения сторонних вычислительных усилий.
Сейчас же (в приведенном выше скрипте) в памяти необходимо хранить List<Transform> для каждой монетки и совершать с ним различные манипуляции.
Причем операции с доступом к элементам List<> считаются одними из самых медленных операций, что ставит под вопрос использования списков для нужных мне целей.
Я попыталась запустить у себя приведенный выше скрипт.
При этом мне потребовалось заменить
Transform tr = (Instantiate(prefab_coin, pos, Quaternion.identity)).transform;
на
Transform tr = (Instantiate(prefab_coin, pos, Quaternion.identity)) as Transform;
т.к. в скрипте возникла ошибка компиляции.
Я также заменила
trs[i].Rotate(Vector3.up, speed, Space.World);
на
trs[i].Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
т.к. мне нужно проверить самый тяжелый случай, когда вращение происходит по 3-м осям и мне необходимо использовать Time.deltaTime.
После осуществления всех этих манипуляций и запуска скрипта - префаб с монетками инстанцировался - произошло создание заданного количества монеток.
Но список List<> так и оказался незаполненным.
Соответственно в методе Update() у меня ничего не происходит - монетки не вращаются.
Поэтому сделать какие-либо выводы я не могу.
В связи с этим возникает еще несколько дополнительных вопросов.
1. Как исправить ситуации с учетом того, что, возможно, использование List<Transform> - не самый оптимальный вариант?
2. Как все-таки запустить этот тестовый скрипт, чтобы инстанцированные монетки вращались с использованием расчета только одного Transform?
3. Как можно корректно сравнить исходный и модифицированный вариант?
4. Какие метрики производительности для этого нужно использовать?
Насколько я понимаю, необходимо сравнивать не процентное соотношение, заключающееся в том, насколько использование исходного или модифицированного скрипта для расчета вращения монеток грузят процессор.
Нужно понимать насколько загружен процессор в общем для исходного и модифицированного вариантов.
Т.е. возможен, например, случай, когда исходный вариант тратит ресурсы процессора на 50% (условная цифра), а модифицированный вариант - на 75% (тоже условная цифра).
Хотя при этом в исходном варианте процентное соотношение вклада вычислительной нагрузки индивидуально вращающего монетку скрипта в общую нагрузку - 10%, а для модифицированного варианта с групповым вращением монеток - 1%.
Но выигрыша от модифицированного варианта все-равно нет, т.к. накладные расходы на хранение, обработку и манипуляцию данными в списке увеличили общую нагрузку на процессор, съели дополнительную оперативную память и т.д.
Я специально утрировала в данном примере цифры, чтобы была видна не очевидность того, что уменьшение процентного соотношения вклада модифицированного скрипта в общую нагрузку дает выигрыш в общей производительности.
5. Как с помощью такой метрик как FPS корректно провести оценку производительности (вычислительных затрат) при сравнении исходного и модифицированного вариантов скриптов, используемых для вращения монеток?
Re: Экономия на работе однотипных скриптов
Добавлено:
29 май 2017, 01:04
DbIMok
В общем-то это общеизвестно
https://blogs.unity3d.com/ru/2015/12/23 ... ate-calls/ Не хотите List, используте массив. Метрики - тестовая сценка и замеры.
Re: Экономия на работе однотипных скриптов
Добавлено:
29 май 2017, 06:46
Valentinus
я думаю имеет смысл вертеть монетки только те что видны на экране.
методами OnBecameVisible() и OnBecameInvisible() устанавливаете bool переменную в true или false, а в Update делаете вращение только если переменная в true.
таким образом у вас из 500 монет будут крутиться может от силы штук 50. то есть уменьшение затрат на вращение - в десять раз.
кроме того, можно вертеть не в Update, который вызывается каждый кадр, а сделать функцию, которую запускать через InvokeRepeat с периодичностью 0.1f секунды (если приращение угла поворота небольшое, то будет плавно, а впрочем, даже если и большое приращение- во время гонки игрок вряд ли будет рассматривать плавность верчения монетки).
это тоже даст уменьшение затрат на вращение в несколько раз.
Re: Экономия на работе однотипных скриптов
Добавлено:
29 май 2017, 07:59
Cr0c
Inessa писал(а):Причем операции с доступом к элементам List<> считаются одними из самых медленных операций, что ставит под вопрос использования списков для нужных мне целей.
Там же по индексу - это не итератор, это быстро.
Inessa писал(а):При этом мне потребовалось заменить
Transform tr = (Instantiate(prefab_coin, pos, Quaternion.identity)).transform;
на
Transform tr = (Instantiate(prefab_coin, pos, Quaternion.identity)) as Transform;
т.к. в скрипте возникла ошибка компиляции.
У меня работает, у Вас тоже всё должно было работать.
Inessa писал(а):Я также заменила
trs[i].Rotate(Vector3.up, speed, Space.World);
на
trs[i].Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
т.к. мне нужно проверить самый тяжелый случай, когда вращение происходит по 3-м осям и мне необходимо использовать Time.deltaTime.
Разницы никакой, всё равно там собирается кватернион.
Re: Экономия на работе однотипных скриптов
Добавлено:
29 май 2017, 13:45
jetyb
В совсем уж жестких случаях (если считать вращение монетки визуальной характеристикой, не привязанной к видео), можно вычислять вращение в вершинном шейдере (наподобие того как вращаются частицы в системе частиц или поворачиваются спрайты):
а. Можно на CPU считать общую матрицу вращения, передавать ее в шейдер (Shader.SerGlobalMatrix) - шейдере умножать и на нее. Недостаток - все монетки будут вращаться с одинаковой фазой.
б. Каждому объекту дать свою уникальную фазу: fi_i число (0, 2pi), в шейдер передавать текущее время t . Вращение считать в вершинном шейдере для каждого объекта индивидуально в зависимости от текущего времени (fi_i + t)