Maneira mais rápida de serializar e desserializar objects .NET

Eu estou procurando o caminho mais rápido para serializar e desserializar objects .NET. Aqui está o que eu tenho até agora:

public class TD { public List CTs { get; set; } public List 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 tData) { var serializer = new XmlSerializer(typeof(List)); TextWriter writer = new StringWriter(); serializer.Serialize(writer, tData); return writer.ToString(); } public static List Deserialize(string tData) { var serializer = new XmlSerializer(typeof(List)); TextReader reader = new StringReader(tData); return (List)serializer.Deserialize(reader); } } 

Aqui está seu modelo (com o inventado CT e TE ) usando protobuf-net (ainda mantendo a capacidade de usar XmlSerializer , o que pode ser útil – em particular para a migration); Eu humildemente submeto (com muita evidência, se você precisar) que este é o serializador de propósito geral mais rápido (ou certamente um dos mais rápidos) do .NET.

Se você precisar de strings, apenas base-64 codifique o binário.

 [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 CTs { get; set; } [XmlElement(Order=2)] public List 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 tData) { using (var ms = new MemoryStream()) { ProtoBuf.Serializer.Serialize(ms, tData); return ms.ToArray(); } } public static List Deserialize(byte[] tData) { using (var ms = new MemoryStream(tData)) { return ProtoBuf.Serializer.Deserialize>(ms); } } } 

Protobuf é muito muito rápido.

Consulte http://code.google.com/p/protobuf-net/wiki/Performance para obter informações detalhadas sobre o desempenho desse sistema e uma implementação.

Ainda outro serializador por aí que afirma ser super rápido é netserializer .

Os dados fornecidos em seu site mostram um desempenho de 2x sobre o protobuf, eu não tentei isso sozinho, mas se você estiver avaliando várias opções, tente também

Tendo interesse nisso, decidi testar os methods sugeridos com o teste mais próximo de “maçãs com maçãs” que pude. Eu escrevi um aplicativo do console, com o seguinte 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(); 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(); var binaryDeserialized = new List(); 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(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(); var protobufDeserialized = new List(); 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(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(); var netSerializerDeserialized = new List(); 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(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(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(byte[] serialized) { using (var stream = new MemoryStream(serialized)) { var result = ProtoBuf.Serializer.Deserialize(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(byte[] serialized) { return Serializer.Deserialize(serialized); } } } } 

Os resultados me surpreenderam; eles eram consistentes quando executados várias vezes:

 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. 

Coletando estes resultados, decidi ver se o ProtoBuf ou o NetSerializer funcionava melhor com objects maiores. Alterei a contagem de coleta para 10.000 objects, mas aumentei o tamanho dos arrays para 1-10.000 em vez de 1-100. Os resultados pareciam ainda mais definitivos:

 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. 

Minha conclusão, portanto, é: pode haver casos em que o ProtoBuf e o NetSerializer sejam adequados, mas em termos de desempenho bruto para objects pelo menos relativamente simples … O BinaryFormatter é significativamente mais eficiente, pelo menos em uma ordem de grandeza.

YMMV.

O serializador binário incluído com .net deve ser mais rápido que o XmlSerializer. Ou outro serializador para protobuf, json, …

Mas para alguns deles você precisa adicionar atributos ou alguma outra maneira de adicionar metadados. Por exemplo, o ProtoBuf usa IDs de propriedade numérica internamente, e o mapeamento precisa ser de alguma forma conservado por um mecanismo diferente. O version control não é trivial com qualquer serializador.

Eu removi os erros no código acima e fiquei abaixo dos resultados: Também não tenho certeza, já que o NetSerializer exige que você registre os tipos que você está serializando, que tipo de compatibilidade ou diferenças de desempenho podem potencialmente fazer.

 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(); 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(); var binaryDeserialized = new List(); 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(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(); var protobufDeserialized = new List(); 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(serialized)); } stopWatch.Stop(); Console.WriteLine("ProtoBuf: Deserializing took {0}ms.", stopWatch.Elapsed.TotalMilliseconds); Console.WriteLine(); Console.WriteLine("Testing NetSerializer serializer..."); var netSerializerSerialized = new List(); var netSerializerDeserialized = new List(); 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(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(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(byte[] serialized) { using (var stream = new MemoryStream(serialized)) { var result = ProtoBuf.Serializer.Deserialize(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(byte[] serialized) { using (var stream = new MemoryStream(serialized)) { Serializer.Deserialize(stream, out var result); return (T)result; } } } } } 

Você pode experimentar o serializador Salar.Bois que tem um desempenho decente. Seu foco está no tamanho da carga útil, mas também oferece bom desempenho.

Existem referências na página do Github se você deseja ver e comparar os resultados por si mesmo.

https://github.com/salarcode/Bois

Tomei a liberdade de alimentar suas classs no gerador CGbR . Como está em um estágio inicial, ele ainda não suporta o DateTime , então eu simplesmente o substitui por muito tempo. O código de serialização gerado se parece com isto:

 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(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(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; } 

Eu criei uma lista de objects de amostra como este:

 var objects = new List(); 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(), Tes = new List() }; 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 na minha máquina no Release build:

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

Tamanho: 149000 bytes

Tempo: 2,059 ms 3,13 ms

Edit: Começando com CGbR 0.4.3 o serializador binário suporta DateTime. Infelizmente, o método DateTime.ToBinary é insanamente lento. Vou substituí-lo com somehting mais rápido em breve.

Edit2: Ao usar o UTC DateTime , invocando o ToUniversalTime() o desempenho é restaurado e é atingido em 1.669ms .