2010-06-25 13 views
11

Tenemos una v.large Dictionary<long,uint> (varios millones de entradas) como parte de una aplicación C# de alto rendimiento. Cuando la aplicación se cierra, serializamos el diccionario en el disco usando BinaryFormatter y MemoryStream.ToArray(). La serialización vuelve en aproximadamente 30 segundos y produce un archivo de aproximadamente 200 MB de tamaño. Cuando intentemos deserializar el diccionario con el siguiente código:Serialización y deserialización V.Large Dictionary en C#

BinaryFormatter bin = new BinaryFormatter(); 
Stream stream = File.Open("filePathName", FileMode.Open); 
Dictionary<long, uint> allPreviousResults = 
    (Dictionary<long, uint>)bin.Deserialize(stream); 
stream.Close(); 

Tarda unos 15 minutos en volver. Hemos probado alternativas y la parte lenta definitivamente es bin.Derserialize(stream), es decir, los bytes se leen desde el disco duro (SSD de alto rendimiento) en menos de 1 segundo.

¿Puede alguien señalar lo que estamos haciendo mal, ya que queremos el tiempo de carga en el mismo orden que el tiempo de guardar?

Saludos, Marc

+0

¿Cuál es el tipo de diccionario? ES DECIR. ¿Es como: Diccionario ? – CodingGorilla

+0

Acabo de darme cuenta de que realmente agregué eso a la publicación original, pero estaba dentro de corchetes triangulares, por lo que no apareció. El diccionario es (long, uint). – MarcF

+0

Hmm. Interesante. Podría haber jurado que habría implicado cadenas: muchas asignaciones de cadenas en el montón. –

Respuesta

11

Puede comprar protobuf-net o simplemente serializarlo usted mismo, que probablemente será el más rápido que pueda obtener.

class Program 
{ 
    public static void Main() 
    { 
     var dico = new Dictionary<long, uint>(); 
     for (long i = 0; i < 7500000; i++) 
     { 
      dico.Add(i, (uint)i); 
     } 

     using (var stream = File.OpenWrite("data.dat")) 
     using (var writer = new BinaryWriter(stream)) 
     { 
      foreach (var key in dico.Keys) 
      { 
       writer.Write(key); 
       writer.Write(dico[key]); 
      } 
     } 

     dico.Clear(); 
     using (var stream = File.OpenRead("data.dat")) 
     using (var reader = new BinaryReader(stream)) 
     { 
      while (stream.Position < stream.Length) 
      { 
       var key = reader.ReadInt64(); 
       var value = reader.ReadUInt32(); 
       dico.Add(key, value); 
      } 
     } 
    } 
} 

tamaño del archivo resultante => 90M bytes (85.8MB).

+0

Simplemente ejecuta este código usando un diccionario con 20M pares clave-valor, produciendo un archivo de 234MB de tamaño. Rendimiento en un i7 (4GHz) - 8GB DDR3 Ram - Vertex 2 SSD Unidad de disco duro: compilación de diccionario y escritura en tiempo de archivo - 2.17secs Diccionario de lectura de archivo y tiempo de reconstrucción - 15.39seg. Si podemos mantener ese tipo de rendimiento, debería funcionar muy bien bien. – MarcF

+0

+1: solución maravillosa :) – Juliet

+0

Acabamos de implementar esta solución en nuestra aplicación real y los resultados fueron similares a los tiempos de rendimiento publicados anteriormente (es decir, excelente). Estaba un poco preocupado de que tener claves no consecutivas pudiera causar un problema pero no estaba justificado (no parece marcar la diferencia). Nuevamente muchas gracias !! – MarcF

2

Es posible que desee utilizar un generador de perfiles para ver si, detrás de las escenas, la deserializer está llevando a cabo un grupo de reflexión sobre la marcha.

Por ahora, si no desea utilizar una base de datos, intente almacenar sus objetos como un archivo plano en un formato personalizado. Por ejemplo, en la primera línea, el archivo proporciona el número total de entradas en el diccionario, lo que le permite crear instancias de un diccionario con un tamaño predeterminado. Tenga las líneas restantes como una serie de pares clave-valor de ancho fijo que representen todas las entradas de su diccionario.

Con su nuevo formato de archivo, use un StreamReader para leer en su archivo línea por línea o en bloques fijos, vea si esto le permite leer en su diccionario más rápido.

+0

Buen punto para dimensionar el diccionario antes de agregar las entradas. Al investigar este enfoque, sugeriría usar un binaryreader \ writer como leer millones de cadenas, crear millones de cadenas y luego analizar millones de longs y ulongs de esas cadenas tendrán sus propios problemas de rendimiento. –

+0

Ver el ejemplo de @ Darin. –

1

Hay varias soluciones rápidas de valor-clave NoSQL por qué no probarlas? Como ejemplo ESENT, alguien lo publicó aquí en SO. managedesent

4

Sólo para mostrar la serialización similar (a la respuesta aceptada) a través de protobuf-net:

using System.Collections.Generic; 
using ProtoBuf; 
using System.IO; 

[ProtoContract] 
class Test 
{ 
    [ProtoMember(1)] 
    public Dictionary<long, uint> Data {get;set;} 
} 

class Program 
{ 
    public static void Main() 
    { 
     Serializer.PrepareSerializer<Test>(); 
     var dico = new Dictionary<long, uint>(); 
     for (long i = 0; i < 7500000; i++) 
     { 
      dico.Add(i, (uint)i); 
     } 
     var data = new Test { Data = dico }; 
     using (var stream = File.OpenWrite("data.dat")) 
     { 
      Serializer.Serialize(stream, data); 
     } 
     dico.Clear(); 
     using (var stream = File.OpenRead("data.dat")) 
     { 
      Serializer.Merge<Test>(stream, data); 
     } 
    } 
} 

Tamaño: 83meg - pero lo más importante, usted no ha tenido que hacer todo a mano, introduciendo loco. Rápido también (será aún más rápido en "v2").

Cuestiones relacionadas