Экономия на работе однотипных скриптов

Общие вопросы о Unity3D

Экономия на работе однотипных скриптов

Сообщение Inessa 28 май 2017, 21:06

В ходе профилирования своей игры я обнаружила следующий момент.

Много вычислительных ресурсов зря тратится на выполнение однотипных скриптов, прикрепленных к однотипным объектам.


У меня по уровню вдоль гоночной трассы программно генерируются и расставляются игровые объекты типа 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);    
        }
}
 
Inessa
UNITрон
 
Сообщения: 160
Зарегистрирован: 13 мар 2013, 11:56

Re: Экономия на работе однотипных скриптов

Сообщение samana 28 май 2017, 21:28

Inessa писал(а):Т.е. чтобы Unity обсчитывал вращение для одной монетки, а применял это вращение для всех остальных монеток?

Вы сами ответили на свой вопрос.
Все монетки должны находится в массиве. В Update один раз рассчитываете вращение, а потом в цикле перебираете все монетки и устанавливаете их поворот таким же.
Аватара пользователя
samana
Адепт
 
Сообщения: 4738
Зарегистрирован: 21 фев 2015, 13:00
Откуда: Днепропетровск

Re: Экономия на работе однотипных скриптов

Сообщение seaman 28 май 2017, 21:45

Зачем иметь сразу все монетки? У Вас весь уровень с 500 монетами на экране виден?
seaman
Адепт
 
Сообщения: 8352
Зарегистрирован: 24 янв 2011, 12:32
Откуда: Самара

Re: Экономия на работе однотипных скриптов

Сообщение Cr0c 28 май 2017, 22:06

Сделал на одном скрипте все монетки - 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% по профайлеру
Аватара пользователя
Cr0c
Адепт
 
Сообщения: 3035
Зарегистрирован: 19 июн 2015, 13:50
Skype: cr0c81

Re: Экономия на работе однотипных скриптов

Сообщение Inessa 29 май 2017, 00:03

Скажите, а какие существуют метрики, чтобы я могла корректно сравнивать проделанные тесты?

Ведь задача оценки выигрыша от проделанной оптимизации многопараметрическая.

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

Сейчас же (в приведенном выше скрипте) в памяти необходимо хранить 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 корректно провести оценку производительности (вычислительных затрат) при сравнении исходного и модифицированного вариантов скриптов, используемых для вращения монеток?
Inessa
UNITрон
 
Сообщения: 160
Зарегистрирован: 13 мар 2013, 11:56

Re: Экономия на работе однотипных скриптов

Сообщение DbIMok 29 май 2017, 01:04

В общем-то это общеизвестно https://blogs.unity3d.com/ru/2015/12/23 ... ate-calls/ Не хотите List, используте массив. Метрики - тестовая сценка и замеры.
правильный вопрос - половина ответа. учитесь формулировать вопросы понятно.
Новости > _Telegram чат @unity3d_ru (11.6k/4.8k online) > _Telegram канал @unity_news (4.7k подписчиков) > Телеграм тема > "Спасибо"
Аватара пользователя
DbIMok
Адепт
 
Сообщения: 6372
Зарегистрирован: 31 июл 2009, 14:05

Re: Экономия на работе однотипных скриптов

Сообщение Valentinus 29 май 2017, 06:46

я думаю имеет смысл вертеть монетки только те что видны на экране.
методами OnBecameVisible() и OnBecameInvisible() устанавливаете bool переменную в true или false, а в Update делаете вращение только если переменная в true.

таким образом у вас из 500 монет будут крутиться может от силы штук 50. то есть уменьшение затрат на вращение - в десять раз.

кроме того, можно вертеть не в Update, который вызывается каждый кадр, а сделать функцию, которую запускать через InvokeRepeat с периодичностью 0.1f секунды (если приращение угла поворота небольшое, то будет плавно, а впрочем, даже если и большое приращение- во время гонки игрок вряд ли будет рассматривать плавность верчения монетки).
это тоже даст уменьшение затрат на вращение в несколько раз.
Последний раз редактировалось Valentinus 29 май 2017, 08:13, всего редактировалось 1 раз.
другое мое творчество: samlib.ru/u/us_w/
Аватара пользователя
Valentinus
Старожил
 
Сообщения: 889
Зарегистрирован: 24 мар 2017, 18:33

Re: Экономия на работе однотипных скриптов

Сообщение Cr0c 29 май 2017, 07:59

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.

Разницы никакой, всё равно там собирается кватернион.
Аватара пользователя
Cr0c
Адепт
 
Сообщения: 3035
Зарегистрирован: 19 июн 2015, 13:50
Skype: cr0c81

Re: Экономия на работе однотипных скриптов

Сообщение jetyb 29 май 2017, 13:45

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

а. Можно на CPU считать общую матрицу вращения, передавать ее в шейдер (Shader.SerGlobalMatrix) - шейдере умножать и на нее. Недостаток - все монетки будут вращаться с одинаковой фазой.

б. Каждому объекту дать свою уникальную фазу: fi_i число (0, 2pi), в шейдер передавать текущее время t . Вращение считать в вершинном шейдере для каждого объекта индивидуально в зависимости от текущего времени (fi_i + t)
jetyb
Адепт
 
Сообщения: 1486
Зарегистрирован: 31 окт 2011, 17:21


Вернуться в Общие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: GoGo.Ru [Bot], Google [Bot] и гости: 15