fija cruz astrologia c# .net performance struct immutability

c# - cruz fija astrologia



¿Por qué está bien que esta estructura sea mutable? ¿Cuándo son aceptables las estructuras mutables? (5)

Eric Lippert me dijo que debería "intentar siempre hacer que los tipos de valor sean inmutables" , así que pensé que debería intentar siempre hacer los tipos de valor inmutables.

Pero, acabo de encontrar esta estructura mutable interna, System.Web.Util.SimpleBitVector32 , en el ensamblado System.Web , lo que me hace pensar que debe haber una buena razón para tener una estructura mutable. Supongo que la razón por la que lo hicieron de esta manera es porque se desempeñó mejor en las pruebas, y lo mantuvieron interno para desalentar su uso indebido. Sin embargo, eso es especulación.

He C & P la fuente de esta estructura. ¿Qué es lo que justifica la decisión de diseño para usar una estructura mutable? En general, ¿qué tipo de beneficios puede obtenerse con el enfoque y cuándo son estos beneficios lo suficientemente significativos como para justificar los perjuicios potenciales?

[Serializable, StructLayout(LayoutKind.Sequential)] internal struct SimpleBitVector32 { private int data; internal SimpleBitVector32(int data) { this.data = data; } internal int IntegerValue { get { return this.data; } set { this.data = value; } } internal bool this[int bit] { get { return ((this.data & bit) == bit); } set { int data = this.data; if (value) this.data = data | bit; else this.data = data & ~bit; } } internal int this[int mask, int offset] { get { return ((this.data & mask) >> offset); } set { this.data = (this.data & ~mask) | (value << offset); } } internal void Set(int bit) { this.data |= bit; } internal void Clear(int bit) { this.data &= ~bit; } }


Dado que la carga útil es un número entero de 32 bits, diría que esto podría haberse escrito fácilmente como una estructura inmutable, probablemente sin impacto en el rendimiento. Si está llamando a un método de mutador que cambia el valor de un campo de 32 bits, o reemplazando una estructura de 32 bits con una nueva estructura de 32 bits, todavía está realizando exactamente las mismas operaciones de memoria.

Probablemente alguien quería algo que actuara como una matriz (en realidad solo eran bits en un entero de 32 bits), por lo que decidieron que querían usar la sintaxis del indexador con ella, en lugar de un método menos obvio .WithTheseBitsChanged () que devuelve una nueva estructura. Como no iba a ser utilizado directamente por nadie fuera del equipo web de MS, y probablemente tampoco por mucha gente, incluso dentro del equipo web, me imagino que tenían un margen de maniobra un poco mayor en las decisiones de diseño que las personas que crean las API públicas.

Entonces, no, probablemente no de esa manera para el rendimiento, probablemente fue solo la preferencia personal de algún programador en el estilo de codificación, y nunca hubo una razón convincente para cambiarlo.

Si está buscando pautas de diseño, no dedicaría demasiado tiempo a buscar un código que no haya sido pulido para el consumo público.


En realidad, si busca todas las clases que contienen BitVector en .NET Framework, encontrará un montón de estas bestias :-)

  • System.Collections.Specialized.BitVector32 (el único público ...)
  • System.Web.Util.SafeBitVector32 (hilo seguro)
  • System.Web.Util.SimpleBitVector32
  • System.Runtime.Caching.SafeBitVector32 (hilo seguro)
  • System.Configuration.SafeBitVector32 (hilo seguro)
  • System.Configuration.SimpleBitVector32

Y si miras here residió el SSCLI (Microsoft Shared Source CLI, también conocido como ROTOR) fuente de System.Configuration.SimpleBitVector32, encontrarás este comentario:

// // This is a cut down copy of System.Collections.Specialized.BitVector32. The // reason this is here is because it is used rather intensively by Control and // WebControl. As a result, being able to inline this operations results in a // measurable performance gain, at the expense of some maintainability. // [Serializable()] internal struct SimpleBitVector32

Creo que esto lo dice todo. Creo que el System.Web.Util es uno más elaborado pero construido sobre la misma base.


Si entiendo correctamente, no puede hacer una estructura inmutable serializable simplemente mediante el uso de SerializableAttribute . Esto se debe a que durante la deserialización, el serializador crea una instancia predeterminada de la estructura y luego establece todos los campos después de la instanciación. Si son de solo lectura, la deserialización fracasará.

Por lo tanto, la estructura tenía que ser mutable, de lo contrario habría sido necesario un complejo sistema de serialización.


Usar una estructura para un vector de 32 o 64 bits como se muestra aquí es razonable, con algunas advertencias:

  1. Yo recomendaría usar un bucle Interlocked.CompareExchange al realizar cualquier actualización de la estructura, en lugar de simplemente usar los operadores booleanos ordinarios directamente. Si un subproceso intenta escribir el bit 3 mientras que otro intenta escribir el bit 8, ninguna operación debe interferir con el otro más allá de retrasarlo un poco. El uso de un bucle Interlocked.CompareExchange evitará la posibilidad de un comportamiento erróneo (el subproceso 1 lee el valor, el subproceso 2 lee el valor antiguo, el subproceso 1 escribe el nuevo valor, el subproceso 2 escribe el valor calculado según el valor anterior y deshace el subproceso 1) sin necesidad de ningún otro tipo de bloqueo.
  2. Deben evitarse los miembros de la estructura, que no sean los que establecen las propiedades, que modifiquen "esto". Es mejor usar un método estático que acepte la estructura como un parámetro de referencia. Invocar un miembro de estructura que modifica "esto" es generalmente idéntico a llamar a un método estático que acepta al miembro como un parámetro de referencia, tanto desde el punto de vista semántico como de rendimiento, pero hay una diferencia clave: si uno intenta pasar una estructura de solo lectura por referencia a un método estático, uno obtendrá un error de compilación. Por el contrario, si se invoca en una estructura de solo lectura un método que modifica "esto", no habrá ningún error del compilador, pero la modificación prevista no ocurrirá. Dado que incluso las estructuras mutables pueden tratarse como de solo lectura en ciertos contextos, es mucho mejor obtener un error de compilación cuando esto sucede que tener un código que se compilará pero no funcionará.

A Eric Lippert le gusta hablar sobre cómo las estructuras mutables son malvadas, pero es importante reconocer su relación con ellas: él es una de las personas en el equipo de C # que se encarga de hacer que las funciones de soporte de idiomas como cierres e iteradores. Debido a algunas decisiones de diseño al principio de la creación de .net, apoyar adecuadamente la semántica de tipo de valor es difícil en algunos contextos; su trabajo sería mucho más fácil si no hubiera ningún tipo de valor mutable. No envidio a Eric por su punto de vista, pero es importante tener en cuenta que algunos principios que pueden ser importantes en el diseño del marco y el lenguaje no son aplicables al diseño de aplicaciones.


SimpleBitVector32 es mutable, sospecho, por las mismas razones por las que BitVector32 es mutable. En mi opinión, la guía inmutable es solo eso, una guía ; sin embargo, uno debería tener una buena razón para hacerlo.

Considere, también, el Dictionary<TKey, TValue> - Dictionary<TKey, TValue> algunos detalles ampliados . La estructura de Entry del diccionario es mutable: puede cambiar TValue en cualquier momento. Pero, Entry lógicamente representa un valor .

La mutabilidad debe tener sentido. Estoy de acuerdo con @JoeWhite: alguien quería algo que actuara como una matriz (en realidad solo eran bits en un entero de 32 bits) ; también que ambas estructuras BitVector fácilmente podrían haber sido ... inmutables .

Pero, como una declaración general, estoy en desacuerdo con que probablemente fue solo la preferencia personal de algún programador en el estilo de codificación y se inclinó más hacia que nunca hubo [ni hay] ninguna razón convincente para cambiarlo . Simplemente conozca y comprenda la responsabilidad de usar una estructura mutable.

Editar
Para que conste, estoy totalmente de acuerdo en que siempre debes tratar de hacer que una estructura sea inmutable. Si encuentra que los requisitos dictan la mutabilidad del miembro, revise la decisión de diseño e involucre a los pares.

Actualizar
Inicialmente, no tenía confianza en mi evaluación del rendimiento al considerar un valor variable tipo v. Inmutable. Sin embargo, como señala @David, Eric Lippert escribe esto :

Hay momentos en que necesita extraer hasta el último bit de rendimiento de un sistema. Y en esos escenarios, a veces tiene que hacer una compensación entre el código que es limpio, puro , robusto, comprensible, predecible, modificable y el código que no es ninguno de los anteriores, pero sorprendentemente rápido.

En negrita, porque una estructura mutable no se ajusta al ideal puro de que una estructura debe ser inmutable . Hay un efecto secundario de escribir una estructura mutable: la capacidad de comprensión y la previsibilidad se ven comprometidas, y Eric continúa explicando:

Los tipos de valores mutables ... se comportan de una manera que muchas personas consideran profundamente contra intuitiva, y por lo tanto hacen que sea más fácil escribir código con errores (o código correcto que se convierte fácilmente en código con errores por accidente). Pero sí, son muy rápidos.

El punto que Eric está haciendo es que usted, como diseñador y / o desarrollador, necesita tomar una decisión consciente e informada . ¿Cómo te informas? Eric lo explica también:

Consideraría la posibilidad de codificar dos soluciones de referencia, una que use estructuras mutables, una que use estructuras inmutables, y ejecutar algunos puntos de referencia realistas centrados en el escenario del usuario. Pero esta es la cuestión: no elijas el más rápido. En su lugar, decida ANTES de ejecutar el punto de referencia cómo la lentitud es inaceptablemente lenta .

Sabemos que alterar un tipo de valor es más rápido que crear un nuevo tipo de valor; pero considerando la corrección :

Si ambas soluciones son aceptables, elija una que sea limpia, correcta y lo suficientemente rápida.

La clave está siendo lo suficientemente rápida para compensar los efectos secundarios de elegir mutable sobre inmutable. Solo tú puedes determinar eso.