2008-10-21 23 views
7

Tengo una clase que necesito para la serialización binaria. La clase contiene un campo de la siguiente manera:Optimización de la serialización binaria para matrices genéricas multidimensionales

private T[,] m_data; 

Estas matrices multidimensionales pueden ser bastante grandes (cientos de miles de elementos) y de cualquier tipo primitivo. Cuando probé la serialización .net estándar en un objeto, el archivo escrito en el disco era grande y creo que .net está almacenando una gran cantidad de datos repetidos sobre los tipos de elementos y posiblemente no tan eficientemente como podría hacerse.

He buscado serializadores personalizados pero no he visto ninguno que se ocupe de matrices genéricas multidimensionales. También experimenté con la compresión .NET incorporada en una matriz de bytes de la secuencia de memoria después de la serialización con cierto éxito, pero no tan rápido/comprimido como esperaba.

Mi pregunta es, ¿debería intentar y escribir un serializador personalizado para serializar de manera óptima esta matriz para el tipo apropiado (esto parece un poco desalentador), o debería usar la serialización .net estándar y agregar compresión?

Cualquier consejo sobre el mejor enfoque sería muy apreciado, o enlaces a recursos que muestran cómo abordar la serialización de una matriz genérica multidimensional - como se menciona existing examples He encontrado que no son compatibles con tales estructuras.

Respuesta

5

Esto es lo que se me ocurrió. El código siguiente crea un int [1000] [10000] y lo escribe utilizando BinaryFormatter en 2 archivos: uno comprimido y otro no.

El archivo comprimido es de 1,19 MB (1,255,339 bytes) desabrochado es 38,2 MB (40,150,034 bytes)

 int width = 1000; 
     int height = 10000; 
     List<int[]> list = new List<int[]>(); 
     for (int i = 0; i < height; i++) 
     { 
      list.Add(Enumerable.Range(0, width).ToArray()); 
     } 
     int[][] bazillionInts = list.ToArray(); 
     using (FileStream fsZ = new FileStream("c:\\temp_zipped.txt", FileMode.Create)) 
     using (FileStream fs = new FileStream("c:\\temp_notZipped.txt", FileMode.Create)) 
     using (GZipStream gz = new GZipStream(fsZ, CompressionMode.Compress)) 
     { 
      BinaryFormatter f = new BinaryFormatter(); 
      f.Serialize(gz, bazillionInts); 
      f.Serialize(fs, bazillionInts); 
     } 

No puedo pensar en una mejor manera/fácil de hacer esto. La versión comprimida es bastante apretada.

Iría con BinaryFormatter + GZipStream. Hacer algo personalizado no sería divertido en absoluto.


[editar por MG] espero que no se ofenda por una edición, pero el uniforme repetida rango (0, ancho) es sesgar las cosas enormemente; cambie a:

 int width = 1000; 
     int height = 10000; 
     Random rand = new Random(123456); 
     int[,] bazillionInts = new int[width, height]; 
     for(int i = 0 ; i < width;i++) 
      for (int j = 0; j < height; j++) 
      { 
       bazillionInts[i, j] = rand.Next(50000); 
      } 

Y pruébelo; verá temp_notZipped.txt a 40MB, temp_zipped.txt a 62MB. No tan atractivo ...

+0

Hay 2 problemas con esto; el primero es que el OP preguntó sobre rectangular (no dentada). Lo más importante es que su compresión se ve afectada por los datos uniformes. Añadiré un deporte al azar para mostrar lo que quiero decir ... –

+2

Por supuesto, no puedes comprimir datos aleatorios. Pero muchos datos significativos pueden comprimirse razonablemente. Entonces, en mi opinión, la compresión puede ser atractiva. –

0

La mejor relación de tamaño de longitud/tamaño de codificación sería codificar su matriz mediante BitConverter, convirtiendo todos los elementos en su formato binario compacto. Es manual, lo sé, pero ahorrará un 80-90% de espacio en comparación con la serialización binaria .NET.

+0

Sin embargo, BitConverter es un dolor para usar con genéricos (necesitaría usar el reflejo, presumiblemente junto con Delegate.CreateDelegate para la eficiencia), y no funciona para todos los tipos (ni siquiera todas las estructuras incorporadas) .. –

0

¿Se puede definir "grande"? El ejemplo 1000x10000xint (otra publicación) sale a 40Mb; y 1000x10000x4 bytes (= int) es 38MB. Como los gastos generales van, eso no es terrible.

¿Qué tipo de datos es T probable que sea? Sólo primativos? Estoy pensando que probablemente podría editar protobuf-net para admitir arrays rectangulares * - pero para mantener algún tipo de compatibilidad con cables probablemente necesitaríamos un encabezado (un byte) por elemento - es decir, 9 MB de sobrecarga para el ejemplo 1000x10000 .

Esto probablemente no vale la pena para cosas como float, double, etc (ya que se almacenan textualmente en "buffers" de protocolo) - pero puede haber un ahorro para cosas como int simplemente debido a la forma en que los paquetes enteros .. . (especialmente si tienden a estar en el lado más pequeño [magnitud]). Finalmente, si T es en realidad objetos como Person etc., entonces debería ser un lote mejor que la serialización binaria, ya que es muy bueno en el empaque de objetos.

No sería trivial para cuerno de zapato en arreglos rectangulares, pero avíseme si esto es algo que le interesaría probar.

*: no lo hace en el momento ya que los "buffers" de protocolo de especificaciones no son compatibles con ellos, pero podemos cortar alrededor que ...

+0

Muchas gracias por su opinión sobre este Marc. Miré en protobuf-net y se ve muy interesante. Para mi clase habrá una gran cantidad de datos repetidos/redundantes, así que estoy pensando que la serialización binaria estándar más la compresión debería ser suficiente. – WillH

0

La razón es necesario que haya tantos datos acerca de la tipos es que su matriz de T podría ser de cualquier tipo, pero más específicamente, T podría ser del tipo SomeBaseClass, y aún podría almacenar SomeDerivedClass en esa matriz, y el deserializador debería saber esto.

Pero estos datos redundantes lo convierten en un buen candidato para la compresión, como han notado otros.

Cuestiones relacionadas