C# массив объектов, очень большой, ищет лучший способ
Итак, в одном из моих проектов, я пытаюсь изменить способ хранения определенных переменных, у меня есть простой массив объектов. Класс, к которому относятся эти объекты:
class Blocks
{
public byte type = Block.Empty;
byte lastblock = Block.Zero;
}
Я планирую добавить к нему больше, в текущем типе класса используется текущее значение объекта, а в последнем блоке используется объект.
я создаю массив следующим образом:
blocks = new Blocks[width * depth * length];
for (int i = 0; i < ((width * length) * depth); i++)
{
blocks[i] = new Blocks();
}
Проблема, с которой я столкнулся, заключается в том, что когда я создаю очень большой массив (512,512,512 или 134217728 для тех из вас, кто не любит математику), массив становится огромным, до 3,5 гигабайт.
Старый способ создания этого массива был немного проще, но гораздо сложнее его расширить, он просто создал массив байтов, представляющих текущий блок, и, кажется, использует только 2 мегабайта оперативной памяти, когда он действительно загружен (что я не получаю, поскольку 134217728 байт должно быть около 134 мегабайт... верно?). Это просто сбивает с толку, что ссылки на объекты могут генерировать гораздо больше оперативной памяти.
Я делаю что-то не так, или я должен просто вернуться к старому способу, которым это было сделано? Я хотел бы, чтобы ссылки на объекты просто потому, что это означает, что все мои переменные находятся в 1 массиве, а не в 4 отдельных массивах, что, кажется, было бы лучше для системы.
РЕДАКТИРОВАТЬ:
Проработав несколько разных способов сделать это, я обнаружил, что изменение
class Blocks
в
struct Blocks
Сделал мир различий, спасибо сообществу за этот приветственный совет для будущего использования, к сожалению, я не хотел просто добавлять два байта в структуру и вызывать это, вот где я остановился, чтобы проверить свой дизайн и завелся с оригинальная проблема. После добавления чего-либо еще в структуру (что-нибудь еще, что есть в моем списке, по крайней мере, означает ссылку на объект игрока или строку имени игрока). Это вызывает исключение нехватки памяти, что означает, что я не смогу использовать эту систему в конце концов.
Но знание того, как это сделать, будет весьма полезным в будущем. За это еще раз спасибо.
6 ответов
Вот что я попробовал с типом структуры вместо типа класса:
public struct Block
{
public byte _current;
public byte _last;
}
public static void RunSnippet()
{
Block[] blocks = new Block[512 * 512 * 512];
for (int i = 0; i < ((512 * 512) * 512); i++)
{
blocks[i] = new Block();
}
}
Фрагмент работал почти мгновенно и съел около 267 МБ оперативной памяти.
Так что дайте struct попробовать, если это возможно.
Вы можете использовать класс List для управления неограниченным количеством объектов. Пожалуйста, посмотрите на ссылку, которую я предоставил. Вы можете добавить бесконечное (ну, теоретически) количество объектов в список.
Используя списки, вы можете легко получить доступ к любому элементу по их индексу. Он также имеет методы для поиска, сортировки и манипулирования объектами, содержащимися в нем.
Если вы используете список, ваш код будет выглядеть примерно так:
List<Blocks> blocks = new List<Blocks>();
for (int i = 0; i < ((width * length) * depth); i++) // The amount of items that you want to add
{
Blocks b = new Blocks();
blocks.Add(b);
}
Вы можете получить доступ к каждому элементу в этом списке следующим образом -
foreach(Blocks b in blocks)
{
// You have the object, do whatever you want
}
Вы можете найти любой конкретный индекс объекта, содержащегося в списке. Посмотрите этот пример метода.
Таким образом, используя список, вы сможете легко управлять большим количеством объектов единообразным способом.
Чтобы узнать больше, иди сюда.
Прочитайте это: Объектные накладные расходы: скрытая стоимость выделения памяти.NET.
Общая стоимость вашего объекта составляет около 16 байтов (в 32-битной системе). (8 байтов для "заголовка", 4 байта для ваших полей, 4 для справки) И 512 * 512 * 512 * 16 = 2.1gb
:-)
Но вы, вероятно, в 64-битной системе, так что это 16 + 8 + 8 = 28, так 4.29gb
,
Структуры - это путь вперед и открывают возможность оптимизации с небезопасной арифметикой кода / указателя.
struct Block
{
byte Current;
byte Last;
}
Block[] blocks = new Block[512 * 512 * 512];
unsafe
{
Block* currentBlock = &blocks;
for (int i = 0; i < (512 * 512) * 512; i++)
{
currentBlock->Current = 0xff;
currentBlock->Last = 0x00;
currentBlock++;
}
}
Конечно, кто-то придет и скажет, что изменчивые структуры - это зло! (Только если вы не знаете, как их использовать)
Вы должны рассмотреть возможность использования "struct" вместо "class".
http://msdn.microsoft.com/en-us/library/ah19swz4(v=vs.71).aspx
"Тип struct подходит для представления легких объектов, таких как Point, Rectangle и Color. Хотя можно представить точку в виде класса, структура в некоторых сценариях более эффективна. Например, если вы объявляете массив из 1000 Точечные объекты, вы будете выделять дополнительную память для ссылки на каждый объект. В этом случае структура дешевле. "
Пожалуйста, опубликуйте свои результаты, если вы попробуете так.
Когда вы создаете массив, вы также создаете экземпляр блоков в каждой ячейке. Вам действительно нужно это сделать?
blocks[i] = new Blocks()
;
Когда вы не создаете экземпляр блока, у вас просто будет массив пустых ссылок. В коде доступа вы можете проверить нулевое значение и вернуть значение по умолчанию. Что-то вроде этого:
if(blocks[i,j] == null) return new Blocks();
else return blocks[i,j];
При написании также проверьте, если есть один, если нет, сначала создайте его. Это должно сэкономить много памяти.
Использование зубчатых массивов или вложенных списков также должно помочь.
С уважением GJ