Le moyen le plus rapide de sérialiser et de désérialiser les objects .NET

Je cherche le moyen le plus rapide de sérialiser et de désérialiser les objects .NET. Voici ce que j’ai jusqu’ici:

public class TD { public List CTs { get; set; } public List TEs { get; set; } public ssortingng Code { get; set; } public ssortingng Message { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public static ssortingng Serialize(List tData) { var serializer = new XmlSerializer(typeof(List)); TextWriter writer = new SsortingngWriter(); serializer.Serialize(writer, tData); return writer.ToSsortingng(); } public static List Deserialize(ssortingng tData) { var serializer = new XmlSerializer(typeof(List)); TextReader reader = new SsortingngReader(tData); return (List)serializer.Deserialize(reader); } } 

Voici votre modèle (avec CT et TE inventés) utilisant protobuf-net (tout en conservant la possibilité d’utiliser XmlSerializer , ce qui peut être utile, en particulier pour la migration); Je soumets humblement (avec beaucoup de preuves si vous en avez besoin) que c’est le sérialiseur polyvalent le plus rapide (ou certainement le plus rapide) de .NET.

Si vous avez besoin de chaînes, codez simplement le binary en base 64.

 [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 ssortingng Code { get; set; } [XmlElement(Order = 4)] public ssortingng 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); } } } 

Une comparaison complète des différents formats que j’ai faite dans cet article:

Juste un échantillon du post- entrer la description de l'image ici

Protobuf est très très rapide.

Voir http://code.google.com/p/protobuf-net/wiki/Performance pour des informations détaillées sur les performances de ce système et une implémentation.

Un autre sérialiseur qui prétend être super rapide est netserializer .

Les données fournies sur leur site montrent des performances de 2x sur protobuf, je ne l’ai pas essayé moi-même, mais si vous évaluez différentes options, essayez ceci aussi

Ayant un intérêt pour cela, j’ai décidé de tester les méthodes suggérées avec le test “pommes à pommes” le plus proche possible. J’ai écrit une application console avec le code suivant:

 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(ssortingng[] 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); } } } } 

Les résultats m’ont surpris; ils étaient cohérents lorsqu’ils étaient exécutés plusieurs fois:

 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. 

En rassemblant ces résultats, j’ai décidé de voir si ProtoBuf ou NetSerializer fonctionnait mieux avec des objects plus gros. J’ai changé le nombre de collections à 10 000 objects, mais la taille des tableaux a été augmentée à 1-10 000 au lieu de 1-100. Les résultats semblaient encore plus définitifs:

 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. 

Ma conclusion est donc la suivante: il peut y avoir des cas où ProtoBuf et NetSerializer sont bien adaptés, mais en termes de performances brutes pour des objects au moins relativement simples … BinaryFormatter est nettement plus performant, au moins d’un ordre de grandeur.

YMMV.

Le sérialiseur binary inclus avec .net devrait être plus rapide que le XmlSerializer. Ou un autre sérialiseur pour protobuf, json, …

Mais pour certains d’entre eux, vous devez append des atsortingbuts ou une autre manière d’append des métadonnées. Par exemple, ProtoBuf utilise des ID de propriétés numériques en interne et le mappage doit être conservé d’une manière ou d’une autre par un mécanisme différent. Le contrôle de version n’est pas sortingvial avec un sérialiseur.

J’ai supprimé les bogues dans le code ci-dessus et j’ai obtenu des résultats inférieurs: Je ne suis pas sûr de savoir comment NetSerializer exige que vous enregissortingez les types que vous sérialisez, quel type de compatibilité ou de différences de performances.

 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. 

Code modifié

 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(ssortingng[] 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; } } } } } 

Vous pouvez essayer le sérialiseur Salar.Bois qui a des performances décentes. Son objective est la taille de la charge utile, mais elle offre également de bonnes performances.

Il y a des points de repère dans la page Github si vous souhaitez voir et comparer les résultats par vous-même.

https://github.com/salarcode/Bois

J’ai pris la liberté de nourrir vos classes dans le générateur CGbR . Comme il est à un stade précoce, il ne prend pas encore en charge DateTime , alors je l’ai simplement remplacé par long. Le code de sérialisation généré ressemble à ceci:

 public int Size { get { var size = 24; // Add size for collections and ssortingngs 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; } 

J'ai créé une liste d'échantillons comme ceci:

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

Résultats sur ma machine dans la Release :

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

Taille: 149000 octets

Temps: 2.059ms 3.13ms

Edit: à partir de CGbR 0.4.3, le sérialiseur binary prend en charge DateTime. Malheureusement, la méthode DateTime.ToBinary est incroyablement lente. Je vais le remplacer par quelque chose de plus rapide bientôt.

Edit2: Lorsque vous utilisez UTC DateTime en ToUniversalTime() les performances sont restaurées et les horloges atteignent 1,669 ms .