2012-03-30 8 views
11

Tengo una enorme cantidad de datos geográficos representados en la estructura de objeto simple que consiste solamente en estructuras. Todos mis campos son de tipo de valor.serialización rápida/deserialización de estructuras

public struct Child 
{ 
    readonly float X; 
    readonly float Y; 
    readonly int myField; 
} 

public struct Parent 
{ 
    readonly int id; 
    readonly int field1; 
    readonly int field2; 
    readonly Child[] children; 
} 

Los datos se chunked muy bien a pequeñas porciones de Parent[] -s. Cada matriz contiene unas pocas instancias principales. Tengo demasiados datos para mantener todo en la memoria, así que tengo que intercambiar estos fragmentos en el disco de un lado a otro. (Un archivo resultaría aproximadamente 2-300KB).

¿Cuál sería la forma más eficiente de serializar/deserializar el Parent[] en un byte[] para volcar en el disco y leer de nuevo? Con respecto a la velocidad, Estoy particularmente interesado en la deserialización rápida, la velocidad de escritura no es tan crítica.

¿Sería simple BinarySerializer lo suficientemente bueno? ¿O debería hackear con StructLayout (see accepted answer)? No estoy seguro si eso funcionaría con el campo de matriz de Parent.children.

ACTUALIZACIÓN: Respuesta a los comentarios - Sí, los objetos son inmutables (código actualizado) y, de hecho, el campo children no es un tipo de valor. 300 KB no suena demasiado, pero tengo montones de archivos así, así que la velocidad sí importa.

+4

_Todos mis campos son de valor type_ - El campo 'children' no es un tipo de valor. –

+1

300 KB es una cantidad pequeña, esta cantidad se deserializa/serializa en 0,1s sin optimizaciones –

+0

¿Son todos sus datos de solo lectura? – usr

Respuesta

10

BinarySerializer es un serializador muy general. No funcionará tan bien como una implementación personalizada.

Afortunadamente para usted, sus datos consisten solo en estructuras. Esto significa que podrá corregir una distribución de estructura para el elemento secundario y simplemente copiar de forma abreviada el conjunto de elementos secundarios utilizando un código inseguro de un byte [] que haya leído del disco.

Para los padres no es tan fácil porque es necesario tratar a los niños por separado. Te recomiendo que uses un código inseguro para copiar los campos de copiado de bits del byte [] que lees y deserializas a los niños por separado.

¿Consideró asociar todos los niños a la memoria utilizando archivos mapeados en la memoria? Luego, puede volver a utilizar la función de caché de los sistemas operativos y no ocuparse en absoluto de la lectura y la escritura.

Zero-copy-deserializar un niño [] es similar al siguiente:

byte[] bytes = GetFromDisk(); 
fixed (byte* bytePtr = bytes) { 
Child* childPtr = (Child*)bytePtr; 
//now treat the childPtr as an array: 
var x123 = childPtr[123].X; 

//if we need a real array that can be passed around, we need to copy: 
var childArray = new Child[GetLengthOfDeserializedData()]; 
for (i = [0..length]) { 
    childArray[i] = childPtr[i]; 
} 
} 
+0

Busqué archivos mapeados en memoria, ¡se ven geniales para la administración de acceso a disco! ¿Podría escribir un ejemplo para el segmento inseguro? ¿Cómo puedo "lanzar" un byte [] a un Niño [] en modo inseguro? Porque como dijiste tardaría cero tiempo. – user256890

+1

Agregué un ejemplo. Si desea una copia cero, necesita cambiar su aplicación para usar punteros o IO inseguro utilizando ReadFile (leer directamente en un elemento secundario existente []). Pero supongo que el único paso de la copia no es nada. Las CPU son buenas en eso. – usr

10

Si no te gusta ir por el escribir su propia ruta serializador, puede utilizar el serializador protobuf.net. Aquí está la salida de un programa de prueba pequeña:

Using 3000 parents, each with 5 children 
BinaryFormatter Serialized in: 00:00:00.1250000 
Memory stream 486218 B 
BinaryFormatter Deserialized in: 00:00:00.1718750 

ProfoBuf Serialized in: 00:00:00.1406250 
Memory stream 318247 B 
ProfoBuf Deserialized in: 00:00:00.0312500 

Debe ser bastante explica por sí mismo. Esto fue solo por una carrera, pero fue bastante indicativo de la velocidad que vi (3-5x).

Para hacer sus estructuras serializables (con protobuf.neto), sólo tiene que añadir los siguientes atributos:

[ProtoContract] 
[Serializable] 
public struct Child 
{ 
    [ProtoMember(1)] public float X; 
    [ProtoMember(2)] public float Y; 
    [ProtoMember(3)] public int myField; 
} 

[ProtoContract] 
[Serializable] 
public struct Parent 
{ 
    [ProtoMember(1)] public int id; 
    [ProtoMember(2)] public int field1; 
    [ProtoMember(3)] public int field2; 
    [ProtoMember(4)] public Child[] children; 
} 

ACTUALIZACIÓN:

En realidad, escribir un serializador personalizado es bastante fácil, aquí es una aplicación escueto:

class CustSerializer 
{ 
    public void Serialize(Stream stream, Parent[] parents, int childCount) 
    { 
     BinaryWriter sw = new BinaryWriter(stream); 
     foreach (var parent in parents) 
     { 
      sw.Write(parent.id); 
      sw.Write(parent.field1); 
      sw.Write(parent.field2); 

      foreach (var child in parent.children) 
      { 
       sw.Write(child.myField); 
       sw.Write(child.X); 
       sw.Write(child.Y); 
      } 
     } 
    } 

    public Parent[] Deserialize(Stream stream, int parentCount, int childCount) 
    { 
     BinaryReader br = new BinaryReader(stream); 
     Parent[] parents = new Parent[parentCount]; 

     for (int i = 0; i < parentCount; i++) 
     { 
      var parent = new Parent(); 
      parent.id = br.ReadInt32(); 
      parent.field1 = br.ReadInt32(); 
      parent.field2 = br.ReadInt32(); 
      parent.children = new Child[childCount]; 

      for (int j = 0; j < childCount; j++) 
      { 
       var child = new Child(); 
       child.myField = br.ReadInt32(); 
       child.X = br.ReadSingle(); 
       child.Y = br.ReadSingle(); 
       parent.children[j] = child; 
      } 

      parents[i] = parent; 
     } 
     return parents; 
    } 
} 

Y aquí es su salida cuando se ejecuta en una prueba de velocidad simple:

Custom Serialized in: 00:00:00 
Memory stream 216000 B 
Custom Deserialized in: 00:00:00.0156250 

Obviamente, es mucho menos flexible que los otros enfoques, pero si la velocidad realmente es tan importante, es aproximadamente 2-3 veces más rápido que el método protobuf. Produce también tamaños de archivo mínimos, por lo que la escritura en el disco debe ser más rápida.

+1

Protobuf es una gran compensación entre la facilidad de uso y el rendimiento en la mayoría de los casos. Si quiere volverse loco, no puede superar el rendimiento de una solución personalizada. ¡Especialmente un poco blittable que puede tener un costo de exactamente cero! – usr