2010-11-10 16 views
67

Estoy buscando la manera más rápida de serializar y deserializar objetos .NET. Aquí es lo que tengo hasta ahora:La forma más rápida de serializar y deserializar objetos .NET

public class TD 
{ 
    public List<CT> CTs { get; set; } 
    public List<TE> TEs { get; set; } 
    public string Code { get; set; } 
    public string Message { get; set; } 
    public DateTime StartDate { get; set; } 
    public DateTime EndDate { get; set; } 

    public static string Serialize(List<TD> tData) 
    { 
     var serializer = new XmlSerializer(typeof(List<TD>)); 

     TextWriter writer = new StringWriter(); 
     serializer.Serialize(writer, tData); 

     return writer.ToString(); 
    } 

    public static List<TD> Deserialize(string tData) 
    { 
     var serializer = new XmlSerializer(typeof(List<TD>)); 

     TextReader reader = new StringReader(tData); 

     return (List<TD>)serializer.Deserialize(reader); 
    }   
} 
+2

rendimiento o el pie de impresión de código? – ulrichb

+0

¿Me está preguntando si necesito datos o código de rendimiento? – aron

+3

Pregunta si, por "forma más rápida", se refiere en términos de rendimiento o en términos de espacio ocupado por el código. 'BinaryFormatter' es extremadamente rápido en términos de código e implementación, pero una solución como la de Marc funcionará más rápido en un benchmark. –

Respuesta

46

Aquí es su modelo (con inventado CT y TE) usando protobuf-net (pero conservando la capacidad de utilizar XmlSerializer, que puede ser útil - en particular para la migración); Presento humildemente (con mucha evidencia si lo necesita) que este es el serializador de propósito general más rápido (o ciertamente uno de los más rápidos) en .NET.

Si necesita cadenas, solo base-64 codifica el binario.

[XmlType] 
public class CT { 
    [XmlElement(Order = 1)] 
    public int Foo { get; set; } 
} 
[XmlType] 
public class TE { 
    [XmlElement(Order = 1)] 
    public int Bar { get; set; } 
} 
[XmlType] 
public class TD { 
    [XmlElement(Order=1)] 
    public List<CT> CTs { get; set; } 
    [XmlElement(Order=2)] 
    public List<TE> TEs { get; set; } 
    [XmlElement(Order = 3)] 
    public string Code { get; set; } 
    [XmlElement(Order = 4)] 
    public string Message { get; set; } 
    [XmlElement(Order = 5)] 
    public DateTime StartDate { get; set; } 
    [XmlElement(Order = 6)] 
    public DateTime EndDate { get; set; } 

    public static byte[] Serialize(List<TD> tData) { 
     using (var ms = new MemoryStream()) { 
      ProtoBuf.Serializer.Serialize(ms, tData); 
      return ms.ToArray(); 
     }    
    } 

    public static List<TD> Deserialize(byte[] tData) { 
     using (var ms = new MemoryStream(tData)) { 
      return ProtoBuf.Serializer.Deserialize<List<TD>>(ms); 
     } 
    } 
} 
+0

gracias Marc por el esfuerzo – aron

+1

G'day Marc, me encanta el trabajo de búferes de protocolo que has hecho y sé que esta publicación tiene casi 5 años, pero el netserializer citado en una respuesta aquí (Binoj) tiene métricas que indican que tu implementación no es el más rápido. ¿Es una declaración/publicidad justa o hay una compensación? gracias –

+0

bien, ahora veo, NetSerialization solo funciona para la misma versión donde estoy buscando Version Tolerant Serialization –

12

Protobuf es muy muy rápido.

Consulte http://code.google.com/p/protobuf-net/wiki/Performance para obtener información detallada sobre el rendimiento de este sistema y una implementación.

+0

¿Hay algún inconveniente al usar Protobuf? –

+7

Tienes que anotar tus objetos. Protobuf no almacena los nombres y tipos de campo como lo hacen los serializadores, sino que los toma de sus tipos reales. Esta es una de las razones por las que los archivos de destino son mucho más pequeños. La documentación explica todo esto. Lo he estado utilizando durante un tiempo, y si necesita serialización rápida (de) y pequeños archivos de destino, protobuf realmente es el camino a seguir. –

+0

¿Alguna muestra de código fuente completo usando Protobut en C# para agregar a la respuesta? – Kiquenet

3

El serializador binario incluido con .net debe ser más rápido que el XmlSerializer. O otro serializador para protobuf, json, ...

Pero para algunos de ellos es necesario agregar Atributos, o de alguna otra manera para agregar metadatos. Por ejemplo, ProtoBuf usa identificadores de propiedades numéricas internamente, y la asignación debe conservarse de alguna manera mediante un mecanismo diferente. El control de versiones no es trivial con ningún serializador.

+0

Sí, es muy rápido, y maneja muchos más casos/tipos que el Xml. – leppie

21

Una comparación entre los formatos comprehansive diffreent hechas por mí en este post http://maxondev.com/serialization-performance-comparison-c-net-formats-frameworks-xmldatacontractserializer-xmlserializer-binaryformatter-json-newtonsoft-servicestack-text/

Sólo una muestra de la post- enter image description here

+2

Eso no es velocidad. Eso es lentitud. Dice que "más pequeño es mejor" en el artículo vinculado. –

+1

@TimurNuriyasov, ese es el tiempo necesario para hacer la operación – Maxim

+2

¿Entonces dices que el binario es el más lento? ¡No lo creo! Supongo que se refiere correctamente a la velocidad, no al tiempo. – Javid

10

Sin embargo, otro serializador por ahí que dice ser súper rápido es netserializer.

Los datos que figuran en su sitio muestra el rendimiento de 2x más protobuf, no he probado esto por mí mismo, pero si usted está evaluando varias opciones, tratar esto también

+2

Acabo de probar NetSerializer en mi aplicación y funciona de maravilla. Vale la pena intentarlo. – Galen

0

Usted puede intentar Salar.Bois serializador que tiene un rendimiento decente . Se centra en el tamaño de la carga pero también ofrece un buen rendimiento.

Hay puntos de referencia en la página de Github si desea ver y comparar los resultados usted mismo.

https://github.com/salarcode/Bois

0

he tomado la libertad de alimentar a sus clases en el CGbR generator. Debido a que se encuentra en una etapa inicial, todavía no es compatible con DateTime, por lo que simplemente lo reemplacé por largo. El código de serialización generado es el siguiente:

public int Size 
{ 
    get 
    { 
     var size = 24; 
     // Add size for collections and strings 
     size += Cts == null ? 0 : Cts.Count * 4; 
     size += Tes == null ? 0 : Tes.Count * 4; 
     size += Code == null ? 0 : Code.Length; 
     size += Message == null ? 0 : Message.Length; 

     return size;    
    } 
} 

public byte[] ToBytes(byte[] bytes, ref int index) 
{ 
    if (index + Size > bytes.Length) 
     throw new ArgumentOutOfRangeException("index", "Object does not fit in array"); 

    // Convert Cts 
    // Two bytes length information for each dimension 
    GeneratorByteConverter.Include((ushort)(Cts == null ? 0 : Cts.Count), bytes, ref index); 
    if (Cts != null) 
    { 
     for(var i = 0; i < Cts.Count; i++) 
     { 
      var value = Cts[i]; 
      value.ToBytes(bytes, ref index); 
     } 
    } 
    // Convert Tes 
    // Two bytes length information for each dimension 
    GeneratorByteConverter.Include((ushort)(Tes == null ? 0 : Tes.Count), bytes, ref index); 
    if (Tes != null) 
    { 
     for(var i = 0; i < Tes.Count; i++) 
     { 
      var value = Tes[i]; 
      value.ToBytes(bytes, ref index); 
     } 
    } 
    // Convert Code 
    GeneratorByteConverter.Include(Code, bytes, ref index); 
    // Convert Message 
    GeneratorByteConverter.Include(Message, bytes, ref index); 
    // Convert StartDate 
    GeneratorByteConverter.Include(StartDate.ToBinary(), bytes, ref index); 
    // Convert EndDate 
    GeneratorByteConverter.Include(EndDate.ToBinary(), bytes, ref index); 
    return bytes; 
} 

public Td FromBytes(byte[] bytes, ref int index) 
{ 
    // Read Cts 
    var ctsLength = GeneratorByteConverter.ToUInt16(bytes, ref index); 
    var tempCts = new List<Ct>(ctsLength); 
    for (var i = 0; i < ctsLength; i++) 
    { 
     var value = new Ct().FromBytes(bytes, ref index); 
     tempCts.Add(value); 
    } 
    Cts = tempCts; 
    // Read Tes 
    var tesLength = GeneratorByteConverter.ToUInt16(bytes, ref index); 
    var tempTes = new List<Te>(tesLength); 
    for (var i = 0; i < tesLength; i++) 
    { 
     var value = new Te().FromBytes(bytes, ref index); 
     tempTes.Add(value); 
    } 
    Tes = tempTes; 
    // Read Code 
    Code = GeneratorByteConverter.GetString(bytes, ref index); 
    // Read Message 
    Message = GeneratorByteConverter.GetString(bytes, ref index); 
    // Read StartDate 
    StartDate = DateTime.FromBinary(GeneratorByteConverter.ToInt64(bytes, ref index)); 
    // Read EndDate 
    EndDate = DateTime.FromBinary(GeneratorByteConverter.ToInt64(bytes, ref index)); 

    return this; 
} 

creé una lista de objetos de la muestra así:

var objects = new List<Td>(); 
for (int i = 0; i < 1000; i++) 
{ 
    var obj = new Td 
    { 
     Message = "Hello my friend", 
     Code = "Some code that can be put here", 
     StartDate = DateTime.Now.AddDays(-7), 
     EndDate = DateTime.Now.AddDays(2), 
     Cts = new List<Ct>(), 
     Tes = new List<Te>() 
    }; 
    for (int j = 0; j < 10; j++) 
    { 
     obj.Cts.Add(new Ct { Foo = i * j }); 
     obj.Tes.Add(new Te { Bar = i + j }); 
    } 
    objects.Add(obj); 
} 

Resultados en mi máquina en Release construcción:

var watch = new Stopwatch(); 
watch.Start(); 
var bytes = BinarySerializer.SerializeMany(objects); 
watch.Stop(); 

Tamaño : 149000 bytes

Tiempo: 2.059ms 3.13ms

Editar: A partir de CGBR 0.4.3 serializador binario apoya DateTime. Lamentablemente, el método DateTime.ToBinary es increíblemente lento. Lo reemplazaré con algo más rápido pronto.

Edit2: Cuando se utiliza UTC DateTime invocando ToUniversalTime() el rendimiento se restaura y los relojes en 1.669ms.

8

Al tener un interés en esto, decidí probar los métodos sugeridos con la prueba más cercana de "manzanas a manzanas" que pude. Escribí una aplicación de consola, con el siguiente código:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.Text; 
using System.Threading.Tasks; 

namespace SerializationTests 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var count = 100000; 
      var rnd = new Random(DateTime.UtcNow.GetHashCode()); 
      Console.WriteLine("Generating {0} arrays of data...", count); 
      var arrays = new List<int[]>(); 
      for (int i = 0; i < count; i++) 
      { 
       var elements = rnd.Next(1, 100); 
       var array = new int[elements]; 
       for (int j = 0; j < elements; j++) 
       { 
        array[j] = rnd.Next(); 
       } 
       arrays.Add(array); 
      } 
      Console.WriteLine("Test data generated."); 
      var stopWatch = new Stopwatch(); 

      Console.WriteLine("Testing BinarySerializer..."); 
      var binarySerializer = new BinarySerializer(); 
      var binarySerialized = new List<byte[]>(); 
      var binaryDeserialized = new List<int[]>(); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var array in arrays) 
      { 
       binarySerialized.Add(binarySerializer.Serialize(array)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("BinaryFormatter: Serializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var serialized in binarySerialized) 
      { 
       binaryDeserialized.Add(binarySerializer.Deserialize<int[]>(serialized)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("BinaryFormatter: Deserializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 


      Console.WriteLine(); 
      Console.WriteLine("Testing ProtoBuf serializer..."); 
      var protobufSerializer = new ProtoBufSerializer(); 
      var protobufSerialized = new List<byte[]>(); 
      var protobufDeserialized = new List<int[]>(); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var array in arrays) 
      { 
       protobufSerialized.Add(protobufSerializer.Serialize(array)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("ProtoBuf: Serializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var serialized in protobufSerialized) 
      { 
       protobufDeserialized.Add(protobufSerializer.Deserialize<int[]>(serialized)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("ProtoBuf: Deserializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      Console.WriteLine(); 
      Console.WriteLine("Testing NetSerializer serializer..."); 
      var netSerializerSerializer = new ProtoBufSerializer(); 
      var netSerializerSerialized = new List<byte[]>(); 
      var netSerializerDeserialized = new List<int[]>(); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var array in arrays) 
      { 
       netSerializerSerialized.Add(netSerializerSerializer.Serialize(array)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("NetSerializer: Serializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var serialized in netSerializerSerialized) 
      { 
       netSerializerDeserialized.Add(netSerializerSerializer.Deserialize<int[]>(serialized)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("NetSerializer: Deserializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      Console.WriteLine("Press any key to end."); 
      Console.ReadKey(); 
     } 

     public class BinarySerializer 
     { 
      private static readonly BinaryFormatter Formatter = new BinaryFormatter(); 

      public byte[] Serialize(object toSerialize) 
      { 
       using (var stream = new MemoryStream()) 
       { 
        Formatter.Serialize(stream, toSerialize); 
        return stream.ToArray(); 
       } 
      } 

      public T Deserialize<T>(byte[] serialized) 
      { 
       using (var stream = new MemoryStream(serialized)) 
       { 
        var result = (T)Formatter.Deserialize(stream); 
        return result; 
       } 
      } 
     } 

     public class ProtoBufSerializer 
     { 
      public byte[] Serialize(object toSerialize) 
      { 
       using (var stream = new MemoryStream()) 
       { 
        ProtoBuf.Serializer.Serialize(stream, toSerialize); 
        return stream.ToArray(); 
       } 
      } 

      public T Deserialize<T>(byte[] serialized) 
      { 
       using (var stream = new MemoryStream(serialized)) 
       { 
        var result = ProtoBuf.Serializer.Deserialize<T>(stream); 
        return result; 
       } 
      } 
     } 

     public class NetSerializer 
     { 
      private static readonly NetSerializer Serializer = new NetSerializer(); 
      public byte[] Serialize(object toSerialize) 
      { 
       return Serializer.Serialize(toSerialize); 
      } 

      public T Deserialize<T>(byte[] serialized) 
      { 
       return Serializer.Deserialize<T>(serialized); 
      } 
     } 
    } 
} 

Los resultados me sorprendieron; eran consistentes cuando se ejecuta varias veces:

Generating 100000 arrays of data... 
Test data generated. 
Testing BinarySerializer... 
BinaryFormatter: Serializing took 336.8392ms. 
BinaryFormatter: Deserializing took 208.7527ms. 

Testing ProtoBuf serializer... 
ProtoBuf: Serializing took 2284.3827ms. 
ProtoBuf: Deserializing took 2201.8072ms. 

Testing NetSerializer serializer... 
NetSerializer: Serializing took 2139.5424ms. 
NetSerializer: Deserializing took 2113.7296ms. 
Press any key to end. 

la recogida de estos resultados, decidí ver si protobuf o NetSerializer obtuvieron mejores resultados con objetos más grandes. Cambié el recuento de colecciones a 10.000 objetos, pero aumenté el tamaño de las matrices a 1-10,000 en lugar de 1-100. Los resultados parecían aún más definitiva:

Generating 10000 arrays of data... 
Test data generated. 
Testing BinarySerializer... 
BinaryFormatter: Serializing took 285.8356ms. 
BinaryFormatter: Deserializing took 206.0906ms. 

Testing ProtoBuf serializer... 
ProtoBuf: Serializing took 10693.3848ms. 
ProtoBuf: Deserializing took 5988.5993ms. 

Testing NetSerializer serializer... 
NetSerializer: Serializing took 9017.5785ms. 
NetSerializer: Deserializing took 5978.7203ms. 
Press any key to end. 

Mi conclusión, por tanto, es: puede haber casos en los que protobuf y NetSerializer se adapta bien a, pero en términos de rendimiento bruto de por lo menos objetos relativamente simples ... BinaryFormatter es significativamente más eficiente, al menos en un orden de magnitud.

YMMV.

+0

tal vez BinaryFormatter es realmente rápido con arreglos. – Behrooz

+2

Es posible ... pero en las condiciones mencionadas, los resultados fueron espectaculares. La lección aquí podría ser, no crean que un método es el más eficiente en todas las circunstancias. Las pruebas y el benchmarking siempre iluminan. –

0

Eliminé los errores en el código anterior y obtuve los siguientes resultados: Tampoco estoy seguro dado que NetSerializer requiere que registre los tipos que está serializando, qué tipo de compatibilidad o diferencias de rendimiento podrían hacer.

Generating 100000 arrays of data... 
Test data generated. 
Testing BinarySerializer... 
BinaryFormatter: Serializing took 508.9773ms. 
BinaryFormatter: Deserializing took 371.8499ms. 

Testing ProtoBuf serializer... 
ProtoBuf: Serializing took 3280.9185ms. 
ProtoBuf: Deserializing took 3190.7899ms. 

Testing NetSerializer serializer... 
NetSerializer: Serializing took 427.1241ms. 
NetSerializer: Deserializing took 78.954ms. 
Press any key to end. 

código modificado

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.Text; 
using System.Threading.Tasks; 

namespace SerializationTests 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var count = 100000; 
      var rnd = new Random((int)DateTime.UtcNow.Ticks & 0xFF); 
      Console.WriteLine("Generating {0} arrays of data...", count); 
      var arrays = new List<int[]>(); 
      for (int i = 0; i < count; i++) 
      { 
       var elements = rnd.Next(1, 100); 
       var array = new int[elements]; 
       for (int j = 0; j < elements; j++) 
       { 
        array[j] = rnd.Next(); 
       } 
       arrays.Add(array); 
      } 
      Console.WriteLine("Test data generated."); 
      var stopWatch = new Stopwatch(); 

      Console.WriteLine("Testing BinarySerializer..."); 
      var binarySerializer = new BinarySerializer(); 
      var binarySerialized = new List<byte[]>(); 
      var binaryDeserialized = new List<int[]>(); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var array in arrays) 
      { 
       binarySerialized.Add(binarySerializer.Serialize(array)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("BinaryFormatter: Serializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var serialized in binarySerialized) 
      { 
       binaryDeserialized.Add(binarySerializer.Deserialize<int[]>(serialized)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("BinaryFormatter: Deserializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 


      Console.WriteLine(); 
      Console.WriteLine("Testing ProtoBuf serializer..."); 
      var protobufSerializer = new ProtoBufSerializer(); 
      var protobufSerialized = new List<byte[]>(); 
      var protobufDeserialized = new List<int[]>(); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var array in arrays) 
      { 
       protobufSerialized.Add(protobufSerializer.Serialize(array)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("ProtoBuf: Serializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var serialized in protobufSerialized) 
      { 
       protobufDeserialized.Add(protobufSerializer.Deserialize<int[]>(serialized)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("ProtoBuf: Deserializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      Console.WriteLine(); 
      Console.WriteLine("Testing NetSerializer serializer..."); 
      var netSerializerSerialized = new List<byte[]>(); 
      var netSerializerDeserialized = new List<int[]>(); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      var netSerializerSerializer = new NS(); 
      foreach (var array in arrays) 
      { 
       netSerializerSerialized.Add(netSerializerSerializer.Serialize(array)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("NetSerializer: Serializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      stopWatch.Reset(); 
      stopWatch.Start(); 
      foreach (var serialized in netSerializerSerialized) 
      { 
       netSerializerDeserialized.Add(netSerializerSerializer.Deserialize<int[]>(serialized)); 
      } 
      stopWatch.Stop(); 
      Console.WriteLine("NetSerializer: Deserializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); 

      Console.WriteLine("Press any key to end."); 
      Console.ReadKey(); 
     } 

     public class BinarySerializer 
     { 
      private static readonly BinaryFormatter Formatter = new BinaryFormatter(); 

      public byte[] Serialize(object toSerialize) 
      { 
       using (var stream = new MemoryStream()) 
       { 
        Formatter.Serialize(stream, toSerialize); 
        return stream.ToArray(); 
       } 
      } 

      public T Deserialize<T>(byte[] serialized) 
      { 
       using (var stream = new MemoryStream(serialized)) 
       { 
        var result = (T)Formatter.Deserialize(stream); 
        return result; 
       } 
      } 
     } 

     public class ProtoBufSerializer 
     { 
      public byte[] Serialize(object toSerialize) 
      { 
       using (var stream = new MemoryStream()) 
       { 
        ProtoBuf.Serializer.Serialize(stream, toSerialize); 
        return stream.ToArray(); 
       } 
      } 

      public T Deserialize<T>(byte[] serialized) 
      { 
       using (var stream = new MemoryStream(serialized)) 
       { 
        var result = ProtoBuf.Serializer.Deserialize<T>(stream); 
        return result; 
       } 
      } 
     } 

     public class NS 
     { 
      NetSerializer.Serializer Serializer = new NetSerializer.Serializer(new Type[] { typeof(int), typeof(int[]) }); 

      public byte[] Serialize(object toSerialize) 
      { 
       using (var stream = new MemoryStream()) 
       { 
        Serializer.Serialize(stream, toSerialize); 
        return stream.ToArray(); 
       } 
      } 

      public T Deserialize<T>(byte[] serialized) 
      { 
       using (var stream = new MemoryStream(serialized)) 
       { 
        Serializer.Deserialize(stream, out var result); 
        return (T)result; 
       } 
      } 
     } 
    } 
} 
Cuestiones relacionadas