Szukam najszybszego sposobu serializacji i deserializacji obiektów .NET. Oto, co mam do tej pory:
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);
}
}
BinaryFormatter
jest niezwykle szybki pod względem kodu i implementacji, ale rozwiązanie takie jak Marc będzie działać szybciej w benchmarku.Odpowiedzi:
Oto Twój model (z wynalezionym
CT
iTE
) wykorzystującym protobuf-net (ale zachowujący możliwość używaniaXmlSerializer
, co może być przydatne - w szczególności podczas migracji); Z pokorą twierdzę (z wieloma dowodami, jeśli ich potrzebujesz), że jest to najszybszy (lub z pewnością jeden z najszybszych) serializatorów ogólnego przeznaczenia w .NET.Jeśli potrzebujesz ciągów, po prostu zakoduj plik binarny 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<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); } } }
źródło
Kompleksowe porównanie różnych formatów wykonanych przeze mnie w tym poście https://maxondev.com/serialization-performance-comparison-c-net-formats-frameworks-xmldatacontractserializer-xmlserializer-binaryformatter-json-newtonsoft-servicestack-text/
Tylko jedna próbka z postu
źródło
Zainteresowany tym, postanowiłem przetestować sugerowane metody z najbardziej zbliżonym testem „jabłka do jabłek”. Napisałem aplikację Console z następującym kodem:
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); } } } }
Wyniki mnie zaskoczyły; były spójne, gdy były uruchamiane wielokrotnie:
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.
Zbierając te wyniki, postanowiłem sprawdzić, czy ProtoBuf lub NetSerializer radzą sobie lepiej z większymi obiektami. Zmieniłem liczbę kolekcji na 10 000 obiektów, ale zwiększyłem rozmiar tablic do 1-10 000 zamiast 1-100. Wyniki wydawały się jeszcze bardziej ostateczne:
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.
Mój wniosek jest zatem taki: mogą istnieć przypadki, w których ProtoBuf i NetSerializer są dobrze przystosowane, ale pod względem surowej wydajności dla przynajmniej stosunkowo prostych obiektów ... BinaryFormatter jest znacznie wydajniejszy, przynajmniej o rząd wielkości.
YMMV.
źródło
Protobuf jest bardzo szybki.
Zobacz http://code.google.com/p/protobuf-net/wiki/Performance, aby uzyskać szczegółowe informacje dotyczące wydajności tego systemu i implementacji.
źródło
Kolejnym serializatorem, który twierdzi, że jest super szybki, jest netserializer .
Dane podane na ich stronie pokazują wydajność 2x - 4x nad protobufem , sam tego nie próbowałem, ale jeśli oceniasz różne opcje, spróbuj również tego
źródło
Binarny serializator dołączony do .net powinien być szybszy niż XmlSerializer. Lub inny serializator dla protobuf, json, ...
Ale w przypadku niektórych z nich musisz dodać atrybuty lub w inny sposób dodać metadane. Na przykład ProtoBuf wewnętrznie używa numerycznych identyfikatorów właściwości, a mapowanie musi być w jakiś sposób chronione przez inny mechanizm. Przechowywanie wersji nie jest trywialne w przypadku żadnego serializatora.
źródło
Usunąłem błędy z powyższego kodu i otrzymałem poniższe wyniki: Nie jestem również pewien, biorąc pod uwagę, w jaki sposób NetSerializer wymaga, abyś rejestrował typy, które serializujesz, jakiego rodzaju kompatybilność lub różnice w wydajności mogą potencjalnie spowodować.
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.
Zmodyfikowany kod
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; } } } } }
źródło
Możesz spróbować Salar.Bois serializator który ma przyzwoitą wydajność. Koncentruje się na wielkości ładunku, ale oferuje również dobrą wydajność.
Na stronie Github znajdują się testy porównawcze, jeśli chcesz samodzielnie zobaczyć i porównać wyniki.
https://github.com/salarcode/Bois
źródło
Pozwoliłem sobie nakarmić twoje klasy w Pozwoliłem generatora CGbR .
Ponieważ jest na wczesnym etapie, nie obsługujeWygenerowany kod serializacji wygląda następująco:DateTime
jeszcze , więc po prostu zastąpiłem go długim.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; }
Utworzyłem listę przykładowych obiektów, taką jak ta:
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); }
Wyniki na moim komputerze w
Release
kompilacji:var watch = new Stopwatch(); watch.Start(); var bytes = BinarySerializer.SerializeMany(objects); watch.Stop();
Rozmiar: 149000 bajtów
Czas:
2,059 ms3,13 msEdycja: począwszy od CGbR 0.4.3 serializator binarny obsługuje datę i godzinę. Niestety
DateTime.ToBinary
metoda jest niesamowicie powolna. Wkrótce zastąpię go czymś szybszym.Edycja2: W przypadku korzystania z UTC
DateTime
przez wywołanieToUniversalTime()
wydajności jest przywracana i zegary na 1,669 ms .źródło