Unity AddComponent очень неэффективный и медленный - PullRequest
0 голосов
/ 23 февраля 2020

У меня есть набор данных из почти 120 000 звезд и тестовый набор данных из 215 звезд (взятых из основного набора данных), и я делаю программу, которая читает набор данных и строит графики звезд вокруг трехмерной модели Земли.

  1. Набор данных считывается, а информация хранится в нескольких списках (в одноэлементном скрипте)
  2. Затем скрипт симуляции читает списки (Singleton уничтожается, чтобы сделать программу более эффективной):
void listStars()
{
    int i = 0;
    while (i < (StarDataBank.Instance.NumOfStars))
    {
        int primaryID = int.Parse(StarDataBank.Instance.StarIDID[i]);
        string properName = StarDataBank.Instance.StarName[i];
        string HIPID = StarDataBank.Instance.StarIDHIP[i];
        string HDID = StarDataBank.Instance.StarIDHD[i];
        string HRID = StarDataBank.Instance.StarIDHR[i];
        string GLID = StarDataBank.Instance.StarIDGL[i];
        string BFID = StarDataBank.Instance.StarIDBF[i];
        decimal rightAscension = Convert.ToDecimal(StarDataBank.Instance.StarRA[i]);
        decimal declination = Convert.ToDecimal(StarDataBank.Instance.StarDec[i]);
        decimal Mag;
        decimal CI;
        float scale = 0;
        int r = 0;
        int g = 0;
        int b = 0;

        Decimal.TryParse((StarDataBank.Instance.StarMag[i]), out Mag);
        Decimal.TryParse((StarDataBank.Instance.StarCI[i]), out CI);

        if (PlayerPrefs.GetInt("dynamicSize") == 1)
        {
            if (Mag < -1)
            {
                scale = 77.5f;
            }
            else
            {
                if (Mag > -1 && Mag <= 5)
                {
                    scale = 52.5f;
                }
                else
                {
                    if (Mag > 5 && Mag <= 10)
                    {
                        scale = 32.5f;
                    }
                    else
                    {
                        if (Mag > 10 && Mag <= 15)
                        {
                            scale = 17.5f;
                        }
                        else
                        {
                            if (Mag > 15 && Mag <= 20)
                            {
                                scale = 7.5f;
                            }
                            else
                            {
                                if (Mag > 20 && Mag <= 25)
                                {
                                    scale = 2.5f;
                                }
                            }
                        }
                    }
                }
            }
        } 
        else
        {
            scale = 20;
        }
        StartCoroutine(placeStars(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, Mag, CI, scale));
        i++;
    }
    DestroyImmediate(StarDataBank.Instance.gameObject);
}
Каждая звезда строится следующим образом:
    IEnumerator placeStars(int primaryID, string properName, string HIPID, string HDID, string HRID, string GLID, string BFID, decimal rightAscension, decimal declination, decimal magnitude, decimal colourIndex, float scale)
    {
        var thisStar = (GameObject)Instantiate(prefabStar, transform.position + getVectors(Convert.ToDecimal(rightAscension), Convert.ToDecimal(declination)), Quaternion.identity);
        thisStar.name = (primaryID).ToString();
        thisStar.transform.parent = StarObject.transform;
        thisStar.transform.localScale = new Vector3(scale, scale, scale);
        thisStar.AddComponent<Star>().newStar(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, magnitude, colourIndex);
        yield return null;
    }

Используя меньший набор тестовых данных с 215 звездами, я могу прикрепить класс Star к каждой Инстанцированной звезде.

Но с большим полным набором данных с ~ 120 тыс. Звезд Unity Editor просто зависает после нажатия кнопки воспроизведения. Когда я закомментирую эту строку: thisStar.AddComponent<Star>().newStar(...); Программа работает так, как задумано, каждая звезда наносится на сцену, единственное, что класс Star не привязывается к каждой вновь созданной звезде.

Это означает, что функция AddComponent не очень хорошо работает с моим кодом, особенно когда используется большой набор данных.

Существует ли более эффективный способ присоединения класса Star к каждому экземпляру звезды GameObject?

Кроме того, имеет ли значение, когда я уничтожаю синглтон после того, как я закончу его использовать? И имеет ли значение, что я использовал IEnumerator для создания экземпляра?

EDIT: Вот класс Star:

public class Star : MonoBehaviour
{


    Simulation simulationInstance;


    public int primaryID;  // primary key                                      NEEDS TO BE SET
    public string properName;  // some stars have names                        NEEDS TO BE SET
    public string HIPID;   // ID of star from Hipparcos catalogue              NEEDS TO BE SET
    public string HDID;    // ID of star from Henry Draper catalogue           NEEDS TO BE SET
    public string HRID;    // ID of star from Harvard Revised catalogue        NEEDS TO BE SET
    public string GLID;    // ID of star from Gliese catalogue                 NEEDS TO BE SET
    public string BFID;    // ID of star from BayerFlamsteed catalogue         NEEDS TO BE SET
    public decimal rightAscension; // right ascension of star                  NEEDS TO BE SET
    public decimal declination;    // declination of star                      NEEDS TO BE SET
    public decimal magnitude;  // magnitude of the star                        NEEDS TO BE SET
    public decimal colourIndex;    // colour index of the star                 NEEDS TO BE SET
    public int scale;  // size of the sphere that will represent the star      AUTOMATICALLY SET
    public int red;    // red colour (0-255)                                   AUTOMATICALLY SET
    public int green;  // green colour (0-255)                                 AUTOMATICALLY SET  
    public int blue;   // blue colour (0-255)                                  AUTOMATICALLY SET
    public double x;   //                                                      AUTOMATICALLY SET
    public double y;   //                                                      AUTOMATICALLY SET
    public double z;   //                                                      AUTOMATICALLY SET

    void Start()
    {
        simulationInstance = FindObjectOfType<Simulation>();
    }

    public void newStar(int primaryID, string properName, string HIPID, string HDID, string HRID, string GLID, string BFID, decimal rightAscension, decimal declination, decimal magnitude, decimal colourIndex)
    {
        this.primaryID = primaryID;
        this.properName = properName;
        this.HIPID = HIPID;
        this.HDID = HDID;
        this.HRID = HRID;
        this.GLID = GLID;
        this.BFID = BFID;
        this.rightAscension = rightAscension;
        this.declination = declination;
        this.magnitude = magnitude;
        this.colourIndex = colourIndex;

    }

    public void tellStarInfoPanel()
    {
        simulationInstance.starInfoPanelManager(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, magnitude, colourIndex);
    }
}

EDIT: я сделал listStars () сопрограммой и placeStars ( ) нормальный метод

    public void placeStars(int primaryID, string properName, string HIPID, string HDID, string HRID, string GLID, string BFID, decimal rightAscension, decimal declination, decimal magnitude, decimal colourIndex, float scale)
    {
        var thisStar = (GameObject)Instantiate(prefabStar, transform.position + getVectors(Convert.ToDecimal(rightAscension), Convert.ToDecimal(declination)), Quaternion.identity);
        thisStar.name = (primaryID).ToString();
        thisStar.transform.parent = StarObject.transform;
        thisStar.transform.localScale = new Vector3(scale, scale, scale);
        thisStar.GetComponent<Star>().newStar(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, magnitude, colourIndex);
        starsRendered++;  
    }

Ответы [ 4 ]

0 голосов
/ 23 февраля 2020

Есть ли более эффективный способ присоединения класса Star к каждому конкретному звезде GameObject?

Да. К вашему префабу Star уже должен быть прикреплен компонент Star. Затем вы можете создать его как var thisStar = (Star)Instantiate(prefabStar, transform.position + getVectors(Convert.ToDecimal(rightAscension), Convert.ToDecimal(declination)), Quaternion.identity);, избегая шага AddComponent.

Кроме того, имеет ли значение, когда я уничтожаю синглтон после его использования?

Нет. Он не потребляет никакой обработки, вы просто освободите немного памяти.

И имеет ли значение то, что я использовал IEnumerator для создания экземпляра?

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

Некоторые вещи, которые вы можете улучшить

  1. Создание звездочек в нескольких кадрах: для этого вы должны включить ваш метод listStars в сопрограмму, делая тип возвращаемого значения IEnumerator и используя yield return placeStars(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, Mag, CI, scale). Возможно, вы захотите создать несколько звездочек в одном кадре, или это может занять некоторое время!
  2. Ваш метод Star Start становится все дороже при создании каждой звезды, лучше передать его в качестве аргумента placeStars метод, получая его только один раз перед звездой l oop.
  3. Вам не нужно получать dynamicSize от PlayerPrefs на каждой итерации. Сохраните его до запуска l oop. То же самое касается экземпляра синглтона. Кэшируйте его до запуска l oop и используйте кэшированную ссылку.

Пример псевдокода:

IEnumerator listStars()
{
    var starDataBank = StarDataBank.Instance;
    var dynamicSize = PlayerPrefs.GetInt("dynamicSize");
    var simulationInstance = FindObjectOfType<Simulation>();
    var starsPerFrame = 10;
    for (int i = 0; i < starDataBank.NumOfStars; i++)
    {
        // do your stuff
        for (int j = 0; j < starsPerFrame; j++)
            placeStars(simulationInstance, primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, Mag, CI, scale));
        yield return null;
    }
    DestroyImmediate(starDataBank.gameObject);
}
0 голосов
/ 23 февраля 2020

Избегайте использования AddComponent в коде, критичном к производительности

Вызов GameObject.AddComponent стоит дорого, особенно в контексте критического к производительности . При каждом добавлении компонента должно происходить следующее:

  1. Поиск сценария компонента в кэше сценариев по имени. Это также может привести к выделению ресурсов, если оно еще не кэшировано.
  2. Выделение памяти для MonoBehaviour.
  3. Уведомление других подключенных компонентов о добавлении нового компонента. Прикрепленные компоненты могут выполнять действия при добавлении известного компонента. Объем выполняемых здесь работ зависит от количества и типа присоединяемых компонентов. Например, твердое тело должно знать, добавлен ли коллайдер.
  4. Запуск метода пробуждения нового компонента. Эта проверка выделит вызовы AddComponent в контексте, критически важном для производительности. Это также помечает вызывающий метод как дорогой, и любое использование вызывающего метода также получит подсветку индикатора производительности.
0 голосов
/ 23 февраля 2020

Вы должны знать, что это не очень хороший вариант использования для модели Monobehaviour / Component OOP. Возможно, вы захотите переключиться на preview Технический стек ECS для этого. Не так давно go Я протестировал подобный случай и обнаружил, что создание экземпляров 100k сущностей из файла csv выполнимо. В этой ситуации мой ввод-вывод жесткого диска был узким местом, а не процессором / памятью (в отличие от того, что происходит с Monobehaviours). Более того, время загрузки можно оптимизировать, предварительно кэшируя необработанные данные в виде сегментов памяти , преобразованных в двоичные файлы . Но я победил в этом - это намного более продвинутое программирование, чем Monobehaviours, и этот технологический стек в настоящий момент находится в нестабильном состоянии перед выпуском.

0 голосов
/ 23 февраля 2020

Если вы считаете, что AddComponent () слишком сложен в вычислительном отношении, было бы целесообразно поближе взглянуть на компонент Start или еще лучше запустить средство профилирования Unity, которое должно точно определить виновника.

Также сделайте Звезда должна быть Компонентом? Может быть, вы можете go обойти эту проблему, просто используя методы ОО.

...