tengo los siguientes tipos de valores: .NETDisposición de .NET tipo de valor en la memoria
[StructLayout(LayoutKind.Sequential)]
public struct Date
{
public UInt16 V;
}
[StructLayout(LayoutKind.Sequential)]
public struct StringPair
{
public String A;
public String B;
public String C;
public Date D;
public double V;
}
tengo código que se pasa un puntero a un tipo de valor de código no administrado, junto con las compensaciones descubiertos llamando al Sistema .Runtime.InteropServices.Marshal.OffsetOf. El código no administrado está rellenando la fecha y los valores dobles.
Las compensaciones que se reportan para la estructura StringPair son exactamente lo que se espera: 0, 8, 16, 24, 32
tengo el siguiente código en una función de prueba:
FieldInfo[] fields = typeof(StringPair).GetFields(BindingFlags.Instance|BindingFlags.Public);
for (int i = 0; i < fields.Length; i++)
{
int offset = System.Runtime.InteropServices.Marshal.OffsetOf(typeof(StringPair), fields[i].Name).ToInt32();
Console.WriteLine(String.Format(" >> field {0} @ offset {1}", fields[i].Name, offset));
}
Que imprime exactamente estas compensaciones.
>> field A @ offset 0
>> field B @ offset 8
>> field C @ offset 16
>> field D @ offset 24
>> field V @ offset 32
entonces tengo algún código de prueba: foreach (StringPair par en pares) { Fecha d = pair.D; double v = pair.V; ...
que tiene la siguiente ensamblador asociado con él en el depurador:
Date d = pair.D;
0000035d lea rax,[rbp+20h]
00000361 add rax,20h
00000367 mov ax,word ptr [rax]
0000036a mov word ptr [rbp+000000A8h],ax
00000371 movzx eax,word ptr [rbp+000000A8h]
00000378 mov word ptr [rbp+48h],ax
double v = pair.V;
0000037c movsd xmm0,mmword ptr [rbp+38h]
00000381 movsd mmword ptr [rbp+50h],xmm0
Se está cargando el campo D en el offset 32 (0x20) y el campo V en el offset 24 (0x38-0x20) El JIT ha cambiado el orden. El depurador de Visual Studio también muestra este orden invertido.
¿Por qué? Me he estado tirando de los pelos tratando de ver dónde va mi lógica. Si cambio el orden de D y V en la estructura, todo funciona, pero este código debe poder tratar con una arquitectura de complemento donde otros desarrolladores hayan definido la estructura, y no se puede esperar que recuerden las reglas de diseño arcano.
Gracias: me había perdido el hecho de que los métodos Marshall. * Solo se aplicaban al puntero estructurado. Por motivos de rendimiento, esperaba evitar una copia adicional de los datos, pero eso parece bastante inevitable si quiero admitir estructuras –
+1 arbitrarias para aclarar que el diseño de la estructura JIT es un problema completamente diferente de lo que CLR especifica, ya que-- en principio, no puede ser visto por, y por lo tanto no puede importar, los programas administrados. Me di cuenta de que JITer parece colocar los campos administrados antes sin administrar. –
El diseño de bits exacto en la memoria de (una instancia de) un tipo de valor puede tener cierta relevancia para decidir si se implementa IEtabletable (y las anulaciones recomendadas de object.Equals y GetHashCode). Si no implementa IEquatable, entonces creo que el código generado por defecto realiza una comparación bit a bit de los bits en la memoria. Si hay GAPS en el diseño, puede perder tiempo comparando bits que no importan (en C++, también podría arriesgarse a la corrección ya que esos bits podrían no inicializarse, pero creo que siempre están en C#). Conocer el diseño informa la decisión sobre qué implementar. –