Самый быстрый способ сериализации и deserialize.NET объекты
Я ищу самый быстрый способ сериализации и десериализации объектов .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);
}
}
9 ответов:
вот ваша модель (с изобретенным
CT
иTE
) через protobuf-net (но сохраняя возможность использоватьXmlSerializer
, что может быть полезно-в частности, для миграции); я смиренно заявляю (с большим количеством доказательств, если вам это нужно), что это и самый быстрый (или, конечно, один из самых быстрых) сериализатор общего назначения в. NET.Если вам нужны строки, просто 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); } } }
осмысленное сравнение между различными форматами, сделанное мной в этом посте- http://maxondev.com/serialization-performance-comparison-c-net-formats-frameworks-xmldatacontractserializer-xmlserializer-binaryformatter-json-newtonsoft-servicestack-text/
только один образец из поста-
Protobuf очень очень быстро.
см.http://code.google.com/p/protobuf-net/wiki/Performance для получения подробной информации о производительности этой системы и реализации.
еще один сериализатор, который утверждает, что он супер быстрый, - это netserializer.
данные, приведенные на их сайте, показывают производительность 2x над protobuf, я сам этого не пробовал, но если вы оцениваете различные варианты, Попробуйте и это
имея интерес к этому, я решил проверить предложенные методы с самым близким тестом "яблоки к яблокам", который я мог. Я написал консольное приложение, со следующим кодом:
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); } } } }
результаты удивили меня; они были последовательны при запуске несколько раз:
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.
собирая эти результаты, я решил посмотреть, лучше ли работает ProtoBuf или NetSerializer с более крупными объектами. Я изменил количество коллекций до 10 000 объектов, но увеличил размер массивы до 1-10 000 вместо 1-100. Результаты казались еще более определенно:
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.
YMMV.
двоичный сериализатор, включенный в .net, должен быть быстрее, чем XmlSerializer. Или другой сериализатор для protobuf, json, ...
но для некоторых из них нужно добавить атрибуты, или какой-то другой способ, чтобы добавить метаданные. Например, ProtoBuf использует числовые идентификаторы свойств внутри, и сопоставление должно быть каким-то образом сохранено другим механизмом. Управление версиями не является тривиальным с любым сериализатором.
Я удалил ошибки в приведенном выше коде и получил ниже результаты: также я не уверен, учитывая, как NetSerializer требует, чтобы вы регистрировали типы, которые вы сериализуете, какие различия в совместимости или производительности, которые потенциально могут сделать.
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.
Изменен Код
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; } } } } }
вы можете попробовать Салар.Буа сериализатор, который имеет достойную производительность. Его внимание на размер текста, но он также предлагает хорошую производительность.
есть тесты на странице Github, если вы хотите увидеть и сравнить результаты самостоятельно.
Я взял на себя смелость кормить ваши классы в генератор CGbR.
потому что на ранней стадии он не поддерживаетсгенерированный код сериализации выглядит так:DateTime
тем не менее, поэтому я просто заменил его на long.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; }
Я создал список объектов вроде этого:
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); }
результаты на моей машине в
Release
build:var watch = new Stopwatch(); watch.Start(); var bytes = BinarySerializer.SerializeMany(objects); watch.Stop();
размер: 149000 байты
время:
2.059 ms3.13 msEdit: начиная с CGbR 0.4.3 двоичный сериализатор поддерживает DateTime. К сожалению,
DateTime.ToBinary
метод безумно медленно. Я буду заменить то только быстрее.Edit2: при использовании UTC
DateTime
ссылкойToUniversalTime()
производительность восстанавливается и часов в 1.669 ms.