2010-10-15 6 views
80

Я собираюсь создать 100 000 объектов в коде. Они небольшие, только с 2 или 3 свойствами. Я положу их в общий список, и когда они будут, я зациклю их и проведу значение a и, возможно, обновит значение b.Структуры в сравнении с классами

Быстро/лучше создавать эти объекты как класс или как struct?

EDIT

а. Свойства являются типами значений (за исключением строки, я думаю?)

b. Может быть, они (мы еще не уверены) есть метод Validate

EDIT 2

Я задавался вопросом: являются ли объекты в куче и в стек переработанного одинаково сборщиком мусора, или делает эту работу другого ?

+2

Есть ли у них только публичные поля, или у них также будут методы? Являются ли типы примитивными типами, например целыми? Будут ли они содержаться в массиве или что-то вроде List ? – JeffFerguson

+0

Если у вас есть сомнения, используйте класс. Если вам нужна автоматическая инициализация в массиве, используйте struct. – leppie

+13

Список изменчивых структур? Следите за велоцираптором. –

ответ

119

Это более быстрое для создания этих объектов как класса или как структуры?

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

Структуры потребляют меньше памяти кучи (потому что они меньше и более легко уплотняются, а не потому, что они «находятся в стеке»). Но они копируют больше, чем копия. Я не знаю, каковы ваши показатели производительности для использования памяти или скорости; здесь есть компромисс, и вы тот, кто знает, что это.

Это лучше для создания этих объектов как класса или как структуры?

Возможно, класс, возможно, структура. Как правило: Если объект является:
1. Маленький
2. Логически неизменное значение
3. Там очень много из них
Тогда я считал бы сделать его структурой. В противном случае я бы придерживался ссылочного типа.

Если вам нужно мутировать какое-либо поле структуры, обычно лучше построить конструктор, который возвращает целую новую структуру с заданным полем правильно. Это, возможно, немного медленнее (измерить это!), Но логически намного легче рассуждать.

Являются ли объекты на куче и стеке одинаково обработаны сборщиком мусора?

Нет, они не то же самое, потому что объекты на стеке корни коллекции. Сборщику мусора не нужно когда-либо спрашивать: «Это живая вещь в стеке?» потому что ответ на этот вопрос всегда «Да, это в стеке». (Теперь вы не можете полагаться на это, чтобы сохранил объект, потому что стек является деталью реализации. Джиттеру разрешено вводить оптимизацию, которая, скажем, регистрирует то, что обычно является значением стека, а затем никогда не будет стек, так что GC не знает, что он все еще жив. Зарегистрированный объект может агрессивно собирать своих потомков, как только регистр, удерживаемый на нем, не будет считаться снова.)

Но сборщик мусора делает должен относиться к объектам в стеке как к живым, так же, как к любому объекту, который, как известно, жив как живой. Объект в стеке может ссылаться на объекты, выделенные кучей, которые необходимо сохранить в живых, поэтому GC должен обрабатывать объекты стека, такие как живые объекты, выделенные кучей, для определения живого набора. Но очевидно, что они не рассматриваются как «живые объекты» для уплотнения кучи, потому что они не находятся в куче в первую очередь.

Это ясно?

+0

Эрик, знаете ли вы, что компилятор или джиттер используют неизменяемость (возможно, если они выполняются с помощью 'readonly'), чтобы разрешить оптимизацию. Я бы не позволил, чтобы это повлияло на выбор на изменчивость (я в качестве ореха для теории эффективности в теории, но на практике мой первый шаг к эффективности всегда пытается иметь как можно больше гарантию правильности, и я не могу отходы циклов ЦП и мозговых циклов на проверках и краях, а также соответственно изменяемые или неизменные помогают), но это будет противодействовать любой реакции коленного рефлекса на вашу неизменность. –

+0

@Jon: компилятор C# оптимизирует * const * данные, но не * readonly * данные.Я не знаю, выполняет ли jit-компилятор какие-либо оптимизации кэширования в полях readonly. –

+0

Жаль, так как я знаю, что знание неизменности позволяет некоторым оптимизациям, но в то же время накладывает ограничения на мои теоретические знания, но они ограничены, которые я бы хотел растянуть. В то же время «это может быть быстрее в обоих направлениях, вот почему, теперь проверяйте и узнайте, что применимо в этом случае» полезно, чтобы иметь возможность сказать :) –

18

Список вернет копию структуры. Для его обновления потребуется , удалив его из списка и снова добавив , создав новую копию с новыми значениями и присвоив ее индексу списка.

Следовательно, лучше использовать классы.

Связанные чтения: Why are mutable structs “evil”?

+5

Именно поэтому вы должны использовать массивы с ценностями, чтобы предотвратить такое копирование :) – leppie

+0

@GSerg прав, также структуры обычно должны быть меньше 16 байт, если структура больше 16 байт, вы можете подумать о том, чтобы сделать ее классом. – TheLukeMcCarthy

+0

+1 для ссылки .... evil и +1 для удаления и добавления информации – Michel

1

Используйте классы.

Об общем примечании. Почему бы не обновить значение b при их создании?

6

Структуры могут казаться похожими на классы, но есть важные отличия, о которых вы должны знать. Прежде всего, классы являются ссылочными типами, а структуры - типами значений. Используя структуры, вы можете создавать объекты, которые ведут себя как встроенные типы, и пользоваться их преимуществами.

Когда вы вызываете оператор New в классе, он будет выделен в куче. Однако при создании экземпляра структуры он создается в стеке. Это даст прирост производительности. Кроме того, вы не будете иметь дело со ссылками на экземпляр структуры, как с классами. Вы будете работать непосредственно со экземпляром struct. Из-за этого при передаче структуры методу она передается по значению, а не как ссылка.

Подробнее здесь:

http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx

+4

Я знаю, что это говорит о нем на MSDN, но MSDN не рассказывает всю историю. Стек и куча - это деталь реализации, а структуры не всегда * идут в стек. Только для одного недавнего блога об этом см .: http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx –

+0

«... это прошло по значению ... "и ссылки и структуры передаются по значению (если только не используется 'ref') - это то, передается ли значение или ссылка, которая отличается, т. е. структуры передаются по значению, объекты класса передаются ссылкой -by-значение и ref отмеченные параметры передают ссылку на ссылку. –

+9

Эта статья вводит в заблуждение по нескольким ключевым моментам, и я попросил команду MSDN пересмотреть или удалить ее. –

6

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

В этом случае, когда вы размещаете их в List<>List<> поддерживается на массиве), было бы более эффективно использовать память в памяти.

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

+0

Вы можете использовать ключевое слово 'ref', чтобы справиться с этим. – leppie

+0

«Остерегайтесь, однако, что большие массивы найдут свой путь в кучке больших объектов, где, если их срок службы длинный, может негативно повлиять на управление памятью вашего процесса». - Я не совсем понимаю, почему ты так думаешь? Выделение на LOH не приведет к каким-либо неблагоприятным последствиям для управления памятью, если только (возможно) это недолговечный объект, и вы хотите быстро восстановить память, не дожидаясь коллекции Gen 2. –

+0

@ Jon Artus: LOH не уплотняется. Любой долгоживущий объект будет разделять LOH на область свободной памяти до и после области. Для выделения требуется непрерывная память, и если эти области недостаточно велики для распределения, тогда больше LMA выделяет больше памяти (то есть вы получите фрагментацию LOH). –

3

Лучшее решение для измерения, измерения снова, затем измерения еще. Могут быть детали того, что вы делаете, что может упростить простой ответ, например «использовать структуры» или «использовать классы».

+0

согласны с частью меры, но, на мой взгляд, это был прямой и ясный пример, и я подумал, что, возможно, об этом можно сказать некоторые общие вещи. И, как выяснилось, некоторые люди это сделали. – Michel

4

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

Что касается вашей второй редакции, то GC имеет дело только с кучей, но есть намного больше места кучи, чем пространство стека, поэтому класть вещи в стек не всегда выигрывает. Кроме того, список структурных типов и список типов классов будут в куче в любом случае, поэтому в этом случае это не имеет значения.

Edit:

Я начинаю рассматривать этот термин зло быть вредным. В конце концов, создание класса mutable - это плохая идея, если он не требуется активно, и я бы не исключил никогда использование измененной структуры. Это плохая идея так часто, что почти всегда бывает плохой идеей, но в основном она просто не совпадает со смысловой семантикой, поэтому просто не имеет смысла использовать структуру в данном случае.

Могут быть разумные исключения с частными вложенными структурами, где, следовательно, все виды использования этой структуры ограничены очень ограниченной областью. Однако это не применяется.

Действительно, я думаю, что «он мутирует, так что это плохой уровень» не намного лучше, чем о куче и стеке (что, по крайней мере, имеет какое-то влияние на производительность, даже если часто искажается). «Он мутирует, так что вполне вероятно, что не имеет смысла считать его имеющим семантику значений, поэтому это плохая структура», это немного отличается, но, что важно, я думаю.

3

Структура в своем сердце не более и не меньше, чем агрегация полей. В .NET возможно, что структура «притворяется» объектом, и для каждого типа структуры .NET неявно определяет тип объекта кучи с теми же полями и методами, которые - будучи объектом кучи - будут вести себя как объект , Переменная, которая содержит ссылку на такой объект кучи («в штучной упаковке»), будет демонстрировать ссылочную семантику, но тот, который непосредственно содержит структуру, представляет собой просто агрегацию переменных.

Я думаю, что большая часть беспорядка между структурами и классами проистекает из того факта, что структуры имеют два очень разных варианта использования, которые должны иметь очень разные принципы проектирования, но рекомендации MS не различают их. Иногда возникает потребность в том, что ведет себя как объект; в этом случае рекомендации MS довольно разумны, хотя «ограничение по 16 байт» должно быть больше похоже на 24-32. Иногда, однако, требуется агрегация переменных. Структура, используемая для этой цели, должна состоять только из кучи открытых полей и, возможно, переопределения Equals, переопределения ToString и IEquatable(itsType).Equals. Структуры, которые используются как скопления полей, не являются объектами и не должны претендовать на роль. С точки зрения структуры, значение поля должно быть не более или менее, чем «последнее, что написано в этом поле». Любое дополнительное значение должно определяться кодом клиента.

Например, если структура с переменной агрегацией имеет члены Minimum и Maximum, сама структура не должна обещать, что Minimum <= Maximum. Код, который получает такую ​​структуру как параметр, должен вести себя так, как если бы он передавался отдельно Minimum и Maximum значений. Требование о том, чтобы Minimum не превышало Maximum, должно рассматриваться как требование, чтобы параметр Minimum был не больше, чем отдельно переданный Maximum.

Полезный шаблон для рассмотрения иногда иметь ExposedHolder<T> класс, определенное что-то вроде:

class ExposedHolder<T> 
{ 
    public T Value; 
    ExposedHolder() { } 
    ExposedHolder(T val) { Value = T; } 
} 

Если один имеет List<ExposedHolder<someStruct>>, где someStruct является структура переменного агрегирования, один может делать такие вещи, как myList[3].Value.someField += 7;, но давая myList[3].Value другому коду, дайте ему содержимое Value, а не дайте ему средство его изменения. В отличие от этого, если использовать List<someStruct>, необходимо использовать var temp=myList[3]; temp.someField += 7; myList[3] = temp;. Если использовать тип изменяемого класса, то для отображения содержимого myList[3] внешнему коду потребуется скопировать все поля на какой-либо другой объект. Если бы использовался тип неизменяемого класса или структура «объект-стиль», было бы необходимо создать новый экземпляр, который был бы как myList[3], за исключением someField, который был другим, а затем сохранил этот новый экземпляр в списке.

Еще одно примечание. Если вы храните большое количество похожих вещей, может быть полезно сохранить их в возможно-вложенных массивах структур, предпочтительно, чтобы сохранить размер каждого массива между 1K и 64K или около того. Массивы структур являются особенными, поскольку индексирование дает прямую ссылку на структуру внутри, поэтому можно сказать «a [12] .x = 5;». Хотя можно определить объекты типа массива, C# не позволяет им использовать такой синтаксис с массивами.

2

С точки зрения C++ я согласен, что это будет медленнее изменять свойства struct по сравнению с классом. Но я думаю, что они будут быстрее читать из-за того, что структура выделяется в стеке вместо кучи. Чтение данных из кучи требует больше проверок, чем из стека.

20

Иногда с struct вам не нужно вызывать конструктор new() и напрямую назначать поля, делающие его намного быстрее обычного.

Пример:

Value[] list = new Value[N]; 
for (int i = 0; i < N; i++) 
{ 
    list[i].id = i; 
    list[i].is_valid = true; 
} 

составляет примерно от 2 до 3 раз быстрее, чем

Value[] list = new Value[N]; 
for (int i = 0; i < N; i++) 
{ 
    list[i] = new Value(i, true); 
} 

, где Value является struct с двумя полями (ID и is_valid).

С другой стороны, элементы должны быть перемещены или выбраны типы значений, все, что копирование будет замедлять работу. Чтобы получить точный ответ, я подозреваю, что вам нужно профилировать свой код и проверить его.

+2

+1 для показа опрятного примера – leppie

+0

Очевидно, что вещи становятся намного быстрее, когда вы также маршали значения над родными границами. – leppie

+0

+1 для примера – Michel

1

Ну, если вы идете со структурой afterall, то избавляйтесь от строки и используйте фиксированный размер char или байтовый буфер.

Это повторно: возможности.

Смежные вопросы