true sirve que para metodo lenguaje flag false booleano bool c# interop

sirve - ¿Cuál es el tamaño de un booleano en C#? ¿Realmente toma 4 bytes?



public bool c# (2)

Tengo dos estructuras con matrices de bytes y booleanos:

using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 4)] struct struct1 { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] values; } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct struct2 { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public bool[] values; }

Y el siguiente código:

class main { public static void Main() { Console.WriteLine("sizeof array of bytes: "+Marshal.SizeOf(typeof(struct1))); Console.WriteLine("sizeof array of bools: " + Marshal.SizeOf(typeof(struct2))); Console.ReadKey(); } }

Eso me da el siguiente resultado:

sizeof array of bytes: 3 sizeof array of bools: 12

Parece ser que un boolean toma 4 bytes de almacenamiento. Idealmente, un boolean solo tomaría un bit ( false o true , 0 o 1 , etc.).

¿Que está sucediendo aquí? ¿Es el tipo boolean realmente tan ineficiente?


El tipo bool tiene un historial a cuadros con muchas opciones incompatibles entre los tiempos de ejecución del idioma. Esto comenzó con una elección histórica de diseño hecha por Dennis Ritchie, el tipo que inventó el lenguaje C. No tenía un tipo bool , la alternativa era int donde un valor de 0 representa falso y cualquier otro valor se considera verdadero .

Esta elección se llevó a cabo en Winapi, la razón principal para usar pinvoke, tiene un typedef para BOOL que es un alias para la palabra clave int del compilador de C. Si no aplica un atributo explícito [MarshalAs], un bool de C # se convierte en BOOL, lo que genera un campo de 4 bytes de longitud.

Hagas lo que hagas, tu declaración de estructura debe coincidir con la elección de tiempo de ejecución realizada en el idioma con el que interopera. Como se señaló, BOOL para el winapi, pero la mayoría de las implementaciones de C ++ eligieron el byte , la mayoría de la interoperabilidad de COM utiliza VARIANT_BOOL, que es una abreviatura .

El tamaño real de un bool C # es un byte. Un fuerte objetivo de diseño del CLR es que no puede descubrirlo. El diseño es un detalle de implementación que depende demasiado del procesador. Los procesadores son muy exigentes con los tipos de variables y la alineación, las elecciones incorrectas pueden afectar significativamente el rendimiento y causar errores de tiempo de ejecución. Al hacer que el diseño no se pueda descubrir, .NET puede proporcionar un sistema de tipo universal que no depende de la implementación real del tiempo de ejecución.

En otras palabras, siempre debe ordenar una estructura en tiempo de ejecución para definir el diseño. En ese momento se realiza la conversión del diseño interno al diseño de interoperabilidad. Eso puede ser muy rápido si el diseño es idéntico, lento cuando los campos necesitan ser reorganizados ya que eso siempre requiere crear una copia de la estructura. El término técnico para esto es " blittable" , pasar una estructura "blittable" a código nativo es rápido porque el jefe de ordenes pinvoke simplemente puede pasar un puntero.

El rendimiento también es la razón principal por la cual un bool no es un solo bit. Hay pocos procesadores que hacen un poco directamente direccionable, la unidad más pequeña es un byte. Se requiere una instrucción adicional para extraer un poco del byte, que no es gratis. Y nunca es atómico.

El compilador de C # no es tímido al decirle que toma 1 byte, use sizeof(bool) . Este aún no es un predictor fantástico de cuántos bytes ocupa un campo en tiempo de ejecución, el CLR también necesita implementar el modelo de memoria .NET y promete que las actualizaciones simples de variables son atómicas . Eso requiere que las variables estén correctamente alineadas en la memoria para que el procesador pueda actualizarlo con un solo ciclo de bus de memoria. Muy a menudo, un bool realmente requiere 4 u 8 bytes en memoria debido a esto. Se agregó relleno adicional para garantizar que el siguiente miembro esté alineado correctamente.

El CLR en realidad aprovecha el diseño que no se puede descubrir, puede optimizar el diseño de una clase y reorganizar los campos para minimizar el relleno. Entonces, digamos, si tiene una clase con un miembro bool + int + bool, entonces tomaría 1 + (3) + 4 + 1 + (3) bytes de memoria, (3) es el relleno, para un total de 12 bytes 50% de residuos. El diseño automático se reorganiza a 1 + 1 + (2) + 4 = 8 bytes. Solo una clase tiene diseño automático, las estructuras tienen diseño secuencial por defecto.

Más sombríamente, un bool puede requerir hasta 32 bytes en un programa C ++ compilado con un compilador moderno de C ++ que admite el conjunto de instrucciones AVX. Lo que impone un requisito de alineación de 32 bytes, la variable bool puede terminar con 31 bytes de relleno. También es la razón principal por la cual un jitter de .NET no emite instrucciones SIMD, a menos que se envuelva explícitamente, no puede obtener la garantía de alineación.


En primer lugar, este es solo el tamaño para interoperabilidad. No representa el tamaño en el código administrado de la matriz. Eso es 1 byte por bool , al menos en mi máquina. Puede probarlo usted mismo con este código:

using System; class Program { static void Main(string[] args) { int size = 10000000; object array = null; long before = GC.GetTotalMemory(true); array = new bool[size]; long after = GC.GetTotalMemory(true); double diff = after - before; Console.WriteLine("Per value: " + diff / size); // Stop the GC from messing up our measurements GC.KeepAlive(array); } }

Ahora, para ordenar matrices por valor, tal como eres, la documentation dice:

Cuando la propiedad MarshalAsAttribute.Value se establece en ByValArray , el campo SizeConst debe establecerse para indicar el número de elementos en la matriz. El campo ArraySubType puede contener opcionalmente el UnmanagedType de los elementos de la matriz cuando sea necesario diferenciar entre los tipos de cadena. Puede usar este UnmanagedType solo en una matriz cuyos elementos aparecen como campos en una estructura.

Entonces miramos ArraySubType , y eso tiene documentación de:

Puede establecer este parámetro en un valor de la enumeración UnmanagedType para especificar el tipo de elementos de la matriz. Si no se especifica un tipo, se utiliza el tipo no administrado predeterminado correspondiente al tipo de elemento de la matriz administrada.

Ahora mirando documentation , hay:

Bool
Un valor booleano de 4 bytes (verdadero! = 0, falso = 0). Este es el tipo Win32 BOOL.

Entonces, ese es el valor predeterminado para bool , y es de 4 bytes porque corresponde al tipo BOOL de Win32, por lo que si interopera con el código que espera una matriz BOOL , hace exactamente lo que desea.

Ahora puede especificar el ArraySubType como I1 , que se documenta como:

Un entero con signo de 1 byte. Puede usar este miembro para transformar un valor booleano en un bool de estilo C de 1 byte (verdadero = 1, falso = 0).

Entonces, si el código con el que interopera espera 1 byte por valor, simplemente use:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)] public bool[] values;

Su código luego lo mostrará como ocupando 1 byte por valor, como se esperaba.