Страница 1 из 2

Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 26 янв 2019, 15:16
AlexandrBuryakov
List не самый производительный массив. (Причину необходимости оптимизировать не обсуждаем.)
По этому принял решение хранить некоторые данные в List, но инициализировать с помощью него обычный массив с фиксированной длинной для повышения производительности.
В определённый момент, иногда реже иногда чаще, в List изменяются, добавляются или удаляются какие то данные. После этого нужно обновить обычный массив.
Я произвожу повторную инициализацию фиксированного массива с новой длинной так:

Синтаксис:
Используется csharp

        //Объявления
        List<Rigidbody> rbList = new List<Rigidbody> ();
        Rigidbody [ ] rbArray;
        int rbMax;

        //Метод инициализации и повторной инициализации
        public void RbArrayReBuf (){

                rbMax = rbList.Count;                           //Максимум rb
                rbArray = new Rigidbody [ rbMax ];              //Создаём массив
                rbList.CopyTo ( rbArray );                      //Копируем в массив

        }

 


Я не знаю, что творится при инициализации массива с новой длинной, потому вопрос такой, что может плохого происходить от периодической переинициализации массива с новой длинной? Не будет утечки памяти или ещё чего? И просто интересно как устраивается новая длинна массива в памяти и как переопределяется ссылка на него, если начальный адрес массива изменился из-за изменения длинны которая не позволяет массиву существовать в той области памяти в которой он был, если это конечно так происходит, сразу говорю, нич*рта в этом не смыслю, работал много с микроконтроллерами, а там всё проще и прозрачнее.

Основной смыл вопроса в том, не будет ли утечки или ещё какой проблемы от этого способа?

Спасибо.

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 26 янв 2019, 20:06
~AvA~
Даже страшно начинать отвечать.. )

Давай всё же не будем обходить стороной и обсудим, какие проблемы с производительностью у листа?
А также подумаем заодно, почему его можно создавать с заданной Capacity, что это решает?
Ответ на этот вопрос в принципе порешает твои проблемы и серьёзно упростит код! )

А то ты спрашиваешь об утечках, при копирровании из листа в массив.. прям .. ну я не знаю, кто-то менее нервный чем я может ответить? :))

P.S: Ты же в курсе, что то, что ты делаешь - это изобретаешь лист?

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 26 янв 2019, 23:39
seaman
Ты же в курсе, что то, что ты делаешь - это изобретаешь лист?

+ 100500

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 27 янв 2019, 14:28
Cr0c
Лист инпаксулирует внутри массив. Главное отличие листа от массива - это проверка индекса при запросе данных и геттер/сеттер при работе с данными.

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 27 янв 2019, 19:20
AlexandrBuryakov
Спасибо за ответ.)
В каждом FixedUpdate мне нужно производить сравнение и вычисление некоторых параметров элементов между собой.
То есть, если элементов 100, то это ~10000 сравнений и вычислений (не будем учитывать оптимизацию избегающую повторений сравнения).
В таком случае простой массив в цикле будет эффективнее чем лист, разве нет? (Где то читал про то, что лист всё таки менее "лёгкий" чем массив)
Я не супер программист, но видел статью которая приводит пример именно в Unity повышения производительности при убирании всех свойств (как раз геттера и сеттера которые, как я только что узнал, есть в List).
Да и статьи про 10000 вызовов тоже видел, где только сами вызовы сжирают кучу ресурсов, если их неймоверно много.
Переинициализироваться List будет редко, потому ~10000 запросов через геттер излишен.

В общем мне тут нужна максимальная производительность, но лезть в дебри ниже C# врятли смогу. И так боюсь всего этого из-за того, что привык к контролю железа, а тут не с могу понять, что под капотом, всё же PC и микроконтроллеры, как песчинка и луна по количеству процессов.
Если переинициализация никак пагубно не влияет, то тогда спасибо за ответ. Интересная информация.)

P.S. Видимо создание собственного примерного List, последствия возни с микроконтроллерами где всё контролируется и всегда изобретается новый велосипед, ибо ресурсов мало.)

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 27 янв 2019, 23:28
Cr0c
~AvA~ писал(а): его можно создавать с заданной Capacity

Вот же ответ - в готовый массив пихать. А по поводу
AlexandrBuryakov писал(а):Да и статьи про 10000 вызовов тоже видел, где только сами вызовы сжирают кучу ресурсов
это было про Update в скриптах, а не вызовы простых гет/сет. С ними критично будет при 1кк вызовах и более без распараллеливания процесса.

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 13 фев 2019, 17:20
lafin
AlexandrBuryakov писал(а):Я не знаю, что творится при инициализации массива с новой длинной, потому вопрос такой, что может плохого происходить от периодической переинициализации массива с новой длинной? Не будет утечки памяти или ещё чего? И просто интересно как устраивается новая длинна массива в памяти и как переопределяется ссылка на него, если начальный адрес массива изменился из-за изменения длинны которая не позволяет массиву существовать в той области памяти в которой он был, если это конечно так происходит, сразу говорю, нич*рта в этом не смыслю, работал много с микроконтроллерами, а там всё проще и прозрачнее.


1. Поле Rigidbody [ ] rbArray это не сам массив, а переменная, хранящая ссылку на массив. "Инициализации массива новой длиной" у тебя нет. Это вообще невозможно. Длина массивов фиксированная, задаётся при создании и никогда не меняется.

Ты просто создаешь новый массив, копируешь в него элементы из списка и записываешь ссылку на этот новый массив в переменную rbArray, затирая ссылку на старый массив. Если на старый массив ссылок нигде больше не хранится, его когда-нибудь "уничтожит" сборщик мусора.

2. Весь метод RbArrayReBuf можно заменить одной строкой: rbArray = rbList.ToArray();

AlexandrBuryakov писал(а):В таком случае простой массив в цикле будет эффективнее чем лист, разве нет? (Где то читал про то, что лист всё таки менее "лёгкий" чем массив)


Запомни простой принцип - никогда не оптимизировать наугад. Даже так: НИКОГДА НЕ ОПТИМИЗИРОВАТЬ НАУГАД. Иначе могут быть сюрпризы.

Если кто-то где-то в интернете написал, что А быстрее Б, это
- не значит, что так на самом деле;
- не значит, что А быстрее Б сейчас (возможно было 5 лет назад);
- не значит, что А быстрее Б везде (в Net Framework, в Net Core, в Mono может быть по-разному):
- не значит, то А быстрее Б в любых задачах или вариантах использования;
- не значит, что А настолько быстрее Б, что имеет смысл менять Б на А везде;
- ...
- может быть, так оно и есть, но кто ж гарантирует?

1. Если веришь, что оптимизированный вариант будет быстрее, надо взять и сравнить. Тогда ты будешь не верить, а точно знать, быстрее ли он и насколько быстрее. Бывает, что медленнее. Бывает, что да, действительно быстрее, но настолько незначительно, что такое ускорение не стоит усложнения кода, которое для него требуется.

2. Если ты думаешь, что знаешь, что оптимизированный вариант будет быстрее, надо взять и сравнить. Тогда ты будешь не думать, что знаешь, а действительно знать.

Если скорость действительно важна, надо сравнивать варианты между собой и принимать решение по результатам сравнения. Если лень, значит скорость не настолько важна - оставь пока как есть.

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

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 22 фев 2019, 19:55
AlexandrBuryakov
Спасибо за объяснения!

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 23 фев 2019, 03:09
Woolf
Cr0c писал(а):Лист инпаксулирует внутри массив. Главное отличие листа от массива - это проверка индекса при запросе данных и геттер/сеттер при работе с данными.


Нууу.. Теоретически это не так. Правильно сделанный List это не массив, это динамическая структура, к массивам не имеющая отношения. Но технически в сишарп лист реализован именно через массивы, что дурость, конечно, несусветная, ибо на корню убивает все преимущества List, а именно, очень быстрое изменение своих размеров, причем, как увеличение, так и уменьшение, без каких либо сдвигов элементов. Но так есть и с этим приходится жить, понимая, что нативный сишарповский лист лучше не уменьшать, иначе будете иметь проблемы с производительностью. Быстрее новый создать на основе старого.

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 23 фев 2019, 11:56
Cr0c
Woolf писал(а):очень быстрое изменение своих размеров, причем, как увеличение, так и уменьшение, без каких либо сдвигов элементов

LinkedList?

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 07 май 2019, 16:11
lafin
Woolf писал(а):Правильно сделанный List это не массив, это динамическая структура, к массивам не имеющая отношения. Но технически в сишарп лист реализован именно через массивы, что дурость, конечно, несусветная, ибо на корню убивает все преимущества List, а именно, очень быстрое изменение своих размеров, причем, как увеличение, так и уменьшение, без каких либо сдвигов элементов. Но так есть и с этим приходится жить, понимая, что нативный сишарповский лист лучше не уменьшать, иначе будете иметь проблемы с производительностью. Быстрее новый создать на основе старого.


Вообще, list/список, если без специальных оговорок, ― это просто упорядоченная коллекция элементов, обычно нефиксированой длины. Остальное ― детали реализации.

У одно/двусвязных реализаций списка есть три большие проблемы, которые сужают их область применения до близкой к нулю в процентном отношении:

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

2. Общие тормоза от того, что элементы списка в общем случае разбросаны по памяти как попало. Ты прочитал один элемент, хочешь прочитать следующий за ним, а он лежит в памяти далеко и отсутствует в кэше процессора. В списке на основе массивов, если ты прочитал один элемент, то соседние почти наверняка тоже сразу окажутся в L1-кэше и доступ к ним будет на два порядка быстрее, чем если читать из памяти.

3. Невозможность доступа к элементу по индексу. В списке из N элементов, чтобы до среднего добраться, надо N/2 других перечитать. И дело не только в том, что их N/2, но ещё смотри пункт 2, в худшем случае умноженный в N/2 раз. В списке же на основе массивов доступ по индексу ― элементарное действие.

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

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

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 07 май 2019, 17:43
~AvA~
То, о чём ты пишешь (связанный список), реализован в с# как класс LinkedList и вообще совсем другое, отличное от того, что здесь советовали использовать..
Говорили мы за стандартный List , который есть массив с изменяющейся длиной (реализовано путём копирования старого массива в новый, с увеличенной длиной) и отлично подходящие для того, что пытается переизобрести ТС

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 07 май 2019, 17:46
~AvA~
А, сорян, ты отвечал Woolf , проморгал ))
А и про линкедлист уже писали, совсем старый стал.. из дискуссии выпал)

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 07 май 2019, 21:23
gnom6584
Не читал полностью тему, и то что тут отвечали, но реиницилизация массива = минус память, уже создавал тему с бомбежкой на этот счет, в итоге мне никто не ответил как это исправить, да и в интернете я чет не особо нашел, как это сделать, даже вызов GC.Collect() не помогает, хотя мой массив был статический, может по-другому все иначе, но вы проверьте все равно

Re: Последствия реинициализация массива с изменением длинны.

СообщениеДобавлено: 07 май 2019, 22:08
~AvA~
gnom6584 писал(а):реиницилизация массива = минус память... но вы проверьте все равно

Пошёл проверил!
Невероятно, но факт - реинициализация создаёт новый объект в памяти, а старый, на которой больше никто не ссылается, тусит в памяти до момента пока его не подгребёт коллектор.. Ну, так работает CLR в .net, а что были сомнения или чего?