Estoy a punto de crear 100.000 objetos en código. Son pequeños, solo con 2 o 3 propiedades. Los pondré en una lista genérica y cuando estén, los haré un bucle y comprobaré el valor a
y tal vez actualizaré el valor b
.
¿Es más rápido / mejor crear estos objetos como clase o como estructura?
EDITAR
a. Las propiedades son tipos de valor (excepto la cadena, ¿creo?)
si. Es posible que (aún no estamos seguros) tengan un método de validación
EDITAR 2
Me preguntaba: ¿los objetos del montón y la pila son procesados por igual por el recolector de basura, o eso funciona de manera diferente?
Respuestas:
Eres la única persona que puede determinar la respuesta a esa pregunta. Pruébalo en ambos sentidos mida una métrica de rendimiento relevante, significativa y centrada en el usuario, y luego sabrá si el cambio tiene un efecto significativo en los usuarios reales en escenarios relevantes.
Las estructuras consumen menos memoria de pila (porque son más pequeñas y se compactan más fácilmente, no porque estén "en la pila"). Pero tardan más en copiarse que una copia de referencia. No sé cuáles son sus métricas de rendimiento para el uso de memoria o la velocidad; aquí hay una compensación y usted es la persona que sabe cuál es.
Tal vez clase, tal vez estructura. Como regla general: si el objeto es:
1. Pequeño
2. Lógicamente un valor inmutable
3. Hay muchos de ellos
Entonces consideraría convertirlo en una estructura. De lo contrario, me quedaría con un tipo de referencia.
Si necesita mutar algún campo de una estructura, generalmente es mejor construir un constructor que devuelva una estructura completamente nueva con el campo configurado correctamente. Eso es quizás un poco más lento (¡mídelo!), Pero lógicamente es mucho más fácil razonar.
No , no son iguales porque los objetos de la pila son las raíces de la colección . El recolector de basura no necesita preguntarse nunca "¿esta cosa en la pila está viva?" porque la respuesta a esa pregunta es siempre "Sí, está en la pila". (Ahora, no puedes confiar en eso para mantener vivo un objeto porque la pila es un detalle de implementación. El jitter puede introducir optimizaciones que, digamos, registran lo que normalmente sería un valor de pila, y luego nunca está en la pila por lo que el GC no sabe que todavía está vivo. Un objeto registrado puede tener sus descendientes recolectados agresivamente, tan pronto como el registro que lo sostiene no se vuelva a leer).
Pero el recolector de basura no tiene que tratar los objetos en la pila como vivos, de la misma manera que trata a cualquier objeto que se sabe que vivo como vivo. El objeto de la pila puede referirse a objetos asignados al montón que deben mantenerse activos, por lo que el GC debe tratar los objetos de la pila como objetos asignados al montón vivos para determinar el conjunto activo. Pero, obviamente, no se tratan como "objetos vivos" a los efectos de compactar el montón, porque en primer lugar no están en el montón.
¿Está claro?
fuente
readonly
) para permitir optimizaciones? No dejaría que eso afecte una elección sobre la mutabilidad (en teoría, soy un loco por los detalles de eficiencia, pero en la práctica, mi primer paso hacia la eficiencia siempre es intentar tener una garantía de corrección tan simple como pueda y, por lo tanto, no tener que hacerlo. desperdiciar ciclos de CPU y ciclos cerebrales en controles y casos extremos, y ser adecuadamente mutable o inmutable ayuda allí), pero contrarrestaría cualquier reacción instintiva a su diciendo que la inmutabilidad puede ser más lenta.A veces con
struct
no es necesario llamar al constructor new () y asignar directamente los campos, lo que lo hace mucho más rápido de lo habitual.Ejemplo:
es aproximadamente de 2 a 3 veces más rápido que
donde
Value
es unstruct
con dos campos (id
yisValid
).Por otro lado, es necesario mover los elementos o seleccionar tipos de valores, todo lo que copiar lo ralentizará. Para obtener la respuesta exacta, sospecho que tienes que perfilar tu código y probarlo.
fuente
list
, dado que el código indicado no funcionará con unList<Value>
.Las estructuras pueden parecer similares a las clases, pero hay diferencias importantes que debe conocer. En primer lugar, las clases son tipos de referencia y las estructuras son tipos de valor. Mediante el uso de estructuras, puede crear objetos que se comporten como los tipos integrados y también disfrutar de sus beneficios.
Cuando llame al operador New en una clase, se asignará en el montón. Sin embargo, cuando crea una instancia de una estructura, se crea en la pila. Esto producirá mejoras en el rendimiento. Además, no tratará con referencias a una instancia de una estructura como lo haría con las clases. Trabajará directamente con la instancia de estructura. Debido a esto, cuando se pasa una estructura a un método, se pasa por valor en lugar de como referencia.
Más aquí:
http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx
fuente
Las matrices de estructuras se representan en el montón en un bloque de memoria contiguo, mientras que una matriz de objetos se representa como un bloque contiguo de referencias con los objetos reales en otra parte del montón, lo que requiere memoria tanto para los objetos como para sus referencias de matriz. .
En este caso, como los está colocando en un
List<>
(y unList<>
está respaldado en una matriz), sería más eficiente, en términos de memoria, usar estructuras.(Sin embargo, tenga en cuenta que las matrices grandes encontrarán su camino en el Montón de objetos grandes donde, si su vida útil es larga, pueden tener un efecto adverso en la gestión de la memoria de su proceso. Recuerde, también, que la memoria no es la única consideración).
fuente
ref
palabras clave para lidiar con esto.Si tienen semántica de valor, probablemente debería usar una estructura. Si tienen semántica de referencia, probablemente debería usar una clase. Hay excepciones, que en su mayoría se inclinan hacia la creación de una clase incluso cuando hay semántica de valores, pero parten de ahí.
En cuanto a su segunda edición, el GC solo se ocupa del montón, pero hay mucho más espacio en el montón que en la pila, por lo que poner cosas en la pila no siempre es una victoria. Además de lo cual, una lista de tipos de estructura y una lista de tipos de clase estarán en el montón de cualquier manera, por lo que esto es irrelevante en este caso.
Editar:
Estoy empezando a considerar que el término maldad es dañino. Después de todo, hacer que una clase sea mutable es una mala idea si no se necesita activamente, y no descartaría usar una estructura mutable. Sin embargo, es una mala idea tan a menudo que casi siempre es una mala idea, pero en su mayoría simplemente no coincide con la semántica de valores, por lo que no tiene sentido usar una estructura en el caso dado.
Puede haber excepciones razonables con estructuras anidadas privadas, donde todos los usos de esa estructura están, por lo tanto, restringidos a un ámbito muy limitado. Sin embargo, esto no se aplica aquí.
Realmente, creo que "muta, por lo que es un mal estudio" no es mucho mejor que hablar del montón y la pila (que al menos tiene algún impacto en el rendimiento, incluso si se tergiversa con frecuencia). "Muta, por lo que es muy probable que no tenga sentido considerar que tiene una semántica de valor, por lo que es una estructura mala" es solo un poco diferente, pero creo que es importante.
fuente
La mejor solución es medir, volver a medir y luego medir un poco más. Puede haber detalles de lo que está haciendo que pueden dificultar una respuesta simplificada y fácil como "usar estructuras" o "usar clases".
fuente
Una estructura es, en el fondo, nada más ni menos que una agregación de campos. En .NET es posible que una estructura "pretenda" ser un objeto, y para cada tipo de estructura .NET define implícitamente un tipo de objeto de montón con los mismos campos y métodos que, al ser un objeto de montón, se comportarán como un objeto. . Una variable que contiene una referencia a dicho objeto de montón (estructura "en caja") exhibirá semántica de referencia, pero una que contiene una estructura directamente es simplemente una agregación de variables.
Creo que gran parte de la confusión estructura versus clase se debe al hecho de que las estructuras tienen dos casos de uso muy diferentes, que deberían tener pautas de diseño muy diferentes, pero las pautas de MS no distinguen entre ellas. A veces existe la necesidad de algo que se comporte como un objeto; en ese caso, las pautas de MS son bastante razonables, aunque el "límite de 16 bytes" probablemente debería ser más como 24-32. A veces, sin embargo, lo que se necesita es una agregación de variables. Una estructura utilizada para ese propósito debería consistir simplemente en un montón de campos públicos, y posiblemente una
Equals
anulación,ToString
anulación yIEquatable(itsType).Equals
implementación. Las estructuras que se utilizan como agregaciones de campos no son objetos y no deberían pretender serlo. Desde el punto de vista de la estructura, el significado de campo debe ser ni más ni menos que "lo último que se escribe en este campo". Cualquier significado adicional debe ser determinado por el código del cliente.Por ejemplo, si una estructura de agregación de variables tiene miembros
Minimum
yMaximum
, la estructura en sí no debería prometer esoMinimum <= Maximum
. El código que recibe dicha estructura como parámetro debe comportarse como si se le pasaran valoresMinimum
y separadosMaximum
. Un requisito queMinimum
no sea mayor queMaximum
debe considerarse como un requisito de que unMinimum
parámetro no sea mayor queMaximum
uno pasado por separado .Un patrón útil a considerar a veces es tener una
ExposedHolder<T>
clase definida como:Si uno tiene
List<ExposedHolder<someStruct>>
, wheresomeStruct
es una estructura de agregación de variables, uno puede hacer cosas comomyList[3].Value.someField += 7;
, pero darlemyList[3].Value
a otro código le dará el contenido enValue
lugar de darle un medio para alterarlo. Por el contrario, si se usaraList<someStruct>
, sería necesario usarvar temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Si uno usa un tipo de clase mutable, exponer el contenido demyList[3]
un código externo requeriría copiar todos los campos a algún otro objeto. Si uno usara un tipo de clase inmutable, o una estructura de "estilo de objeto", sería necesario construir una nueva instancia que fuera comomyList[3]
exceptosomeField
cuál era diferente, y luego almacenar esa nueva instancia en la lista.Una nota adicional: si está almacenando una gran cantidad de cosas similares, puede ser bueno almacenarlas en matrices de estructuras posiblemente anidadas, preferiblemente tratando de mantener el tamaño de cada matriz entre 1K y 64K más o menos. Los arreglos de estructuras son especiales, en el sentido de que indexar uno producirá una referencia directa a una estructura interna, por lo que se puede decir "a [12] .x = 5;". Aunque se pueden definir objetos similares a matrices, C # no les permite compartir dicha sintaxis con matrices.
fuente
Usa clases.
En una nota general. ¿Por qué no actualizar el valor b a medida que los crea?
fuente
Desde una perspectiva de C ++, estoy de acuerdo en que será más lento modificar las propiedades de una estructura en comparación con una clase. Pero sí creo que será más rápido leerlos debido a que la estructura se asigna en la pila en lugar del montón. Leer datos del montón requiere más comprobaciones que de la pila.
fuente
Bueno, si va con struct después de todo, elimine la cadena y use un búfer de bytes o caracteres de tamaño fijo.
Eso es re: rendimiento.
fuente