Я ищу самый быстрый способ сериализации и десериализации .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);
}
}
BinaryFormatter
чрезвычайно быстро с точки зрения кода и реализации, но такое решение, как Marc's, будет работать быстрее в тесте.Ответы:
Вот ваша модель (с изобретенным
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); } } }
источник
Всестороннее сравнение различных форматов, сделанное мной в этом посте - https://maxondev.com/serialization-performance-comparison-c-net-formats-frameworks-xmldatacontractserializer-xmlserializer-binaryformatter-json-newtonsoft-servicestack-text/
Всего один образец из поста
источник
Заинтересовавшись этим, я решил протестировать предложенные методы с помощью максимально приближенного теста «яблоки к яблокам». Я написал консольное приложение со следующим кодом:
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.
Поэтому мой вывод таков: могут быть случаи, когда ProtoBuf и NetSerializer хорошо подходят, но с точки зрения сырой производительности, по крайней мере, для относительно простых объектов ... BinaryFormatter значительно более производительный, по крайней мере, на порядок.
YMMV.
источник
Protobuf очень-очень быстрый.
См. Http://code.google.com/p/protobuf-net/wiki/Performance для получения подробной информации о производительности этой системы и ее реализации.
источник
Еще один сериализатор, который утверждает, что он сверхбыстрый, - это netserializer. .
Данные, приведенные на их сайте, показывают производительность 2x - 4x по сравнению с protobuf , я сам не пробовал, но если вы оцениваете различные варианты, попробуйте и это
источник
Бинарный сериализатор, включенный в .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; } } } } }
источник
Вы можете попробовать Salar.Bois сериализатор который имеет приличную производительность. Он ориентирован на размер полезной нагрузки, но при этом обеспечивает хорошую производительность.
На странице Github есть тесты, если вы хотите увидеть и сравнить результаты самостоятельно.
https://github.com/salarcode/Bois
источник
Я взял на себя смелость скормить ваши классы в скормить генератор 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
сборке:var watch = new Stopwatch(); watch.Start(); var bytes = BinarySerializer.SerializeMany(objects); watch.Stop();
Размер: 149000 байт
Время:
2,059 мс3,13 мсИзменить: начиная с CGbR 0.4.3 двоичный сериализатор поддерживает DateTime. К сожалению, этот
DateTime.ToBinary
метод безумно медленный. Я скоро заменю его чем-нибудь более быстрым.Edit2: при использовании UTC
DateTime
путем вызоваToUniversalTime()
производительности восстанавливается и частота составляет 1,669 мс .источник