97

I'm looking for the fastest way to serialize and deserialize .NET objects. Here is what I have so far:

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);
    }        
}
johnnyRose
  • 7,310
  • 17
  • 40
  • 61
aron
  • 1,874
  • 5
  • 23
  • 29
  • 2
    Performance or code foot print? – ulrichb Nov 10 '10 at 10:34
  • Are you asking me do i need performance data or code? – aron Nov 10 '10 at 10:41
  • 3
    He's asking if, by "fastest way," you mean in terms of performance or in terms of code footprint. `BinaryFormatter` is extremely fast in terms of code and implementation, but a solution like Marc's will perform faster in a benchmark. – Cody Gray - on strike Nov 10 '10 at 11:54
  • ok, i see, i meant in terms of performance... – aron Nov 10 '10 at 11:58
  • There are many links out there. One such: http://blogs.msdn.com/b/youssefm/archive/2009/07/10/comparing-the-performance-of-net-serializers.aspx – nawfal Jul 10 '14 at 10:33
  • _Speed_ is one aspect, produced _size_ is another one, _security_ is a third one. Secure serializers are usually non-polymorphic and may require annotated contracts. But the speed of the serialization itself is negligible anyway, if the payload is retrieved from a network stream, file or database. I will not copy-paste an earlier [answer](https://stackoverflow.com/a/67019995/5114784) of mine so see the link for more thoughts about binary (and any polymorphic) serializers. – György Kőszeg Apr 09 '21 at 12:37

9 Answers9

61

Here's your model (with invented CT and TE) using protobuf-net (yet retaining the ability to use XmlSerializer, which can be useful - in particular for migration); I humbly submit (with lots of evidence if you need it) that this is the fastest (or certainly one of the fastest) general purpose serializer in .NET.

If you need strings, just base-64 encode the binary.

[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);
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    G'day Marc, love the protocol-buffers work you've done and I know this post is almost 5yrs old but the netserializer quoted in an answer here (Binoj) has metrics indicating your implementation isn't the fastest. Is that a fair statement/advertisement or is there a trade off? thanks – Jeremy Thompson Apr 15 '15 at 14:22
  • ok I see now, NetSerialization only works for the same version where as I am looking for Version Tolerant Serialization – Jeremy Thompson Apr 15 '15 at 14:47
  • 1
    Anyone who thinks this is fast must be smoking something, it might be fast enough for a lot of cases, and it might be faster than a lot of other serializes out there, but is it actually fast, compared to manually parsing it? My god no. – BjarkeCK Jun 01 '20 at 10:32
  • 2
    @BjarkeCK serializers are inherently a bit more involved, as they need to do a lot of things to prevent people shooting their own feet off (especially as they iterate versions); most people don't want to spend their lives debugging serialization code, so: a good serializer - while undoubtedly slower than a perfectly implemented version-intolerant manual implementation - is usually a good compromise for most people – Marc Gravell Jun 01 '20 at 11:03
  • 1
    @MarcGravell Absolutely, just wish at least one person in here would have replied with an actual fast approach. We should focus more on the simple stuff, educating people to write good simple code with few abstractions, and less on "go use this library" - which btw usually will cost you time in a lot of other ways.. A lot of the time, you would have been better off writing something yourself, and it's usually a lot easier than people think, and your software also usually end up faster, and you'll end up have a ton of more possibilities because you have control. – BjarkeCK Jun 01 '20 at 12:22
  • 6
    @BjarkeCK I strongly disagree; that's not even *remotely* useful for most folks. What next - writing our own collections every day? No: doing this stuff even reasonably well is *hard*. Sure, if you actually need the very fastest output: you're going to have to get your hands dirty - but for most people, doing this would be a *really* bad waste of their time. **AT BEST** it would take them much much longer. More likely, their code would be buggy, unreliable, and probably slower than using the available libraries. Most people should concentrate on *what their app needs*, not this minutae. – Marc Gravell Jun 01 '20 at 12:35
  • Absolutely not a waste of time! People shouldn't be afraid of getting their hands dirty, it's how you learn! And I think a lot of people will find that sort of thing a lot more rewarding, than wasting time on some generalized API. A lot of the time, you'll be able to make so much better things, by doing them yourself! (Even for collections..) We have so many developers now producing mediocre, bad, and buggy software, adding layers upon layers of abstractions, and have no idea what's going on. Modern software is so god damn buggy and slow these days. – BjarkeCK Jun 01 '20 at 13:34
  • 1
    @BjarkeCK k, we fundamentally disagree here; that's OK; have fun doing it your way – Marc Gravell Jun 01 '20 at 14:56
  • @MarcGravell Hell yeah, entirely ok :) Most likely we develop in very different environments and have different problems. You too! – BjarkeCK Jun 01 '20 at 14:59
35

A comprehensive comparison between different formats made by me in this post- https://medium.com/@maximn/serialization-performance-comparison-xml-binary-json-p-ad737545d227

Just one sample from the post- enter image description here

Moon Waxing
  • 695
  • 13
  • 19
Maxim
  • 7,268
  • 1
  • 32
  • 44
27

Having an interest in this, I decided to test the suggested methods with the closest "apples to apples" test I could. I wrote a Console app, with the following code:

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

The results surprised me; they were consistent when run multiple times:

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.

Collecting these results, I decided to see if ProtoBuf or NetSerializer performed better with larger objects. I changed the collection count to 10,000 objects, but increased the size of the arrays to 1-10,000 instead of 1-100. The results seemed even more definitive:

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.

My conclusion, therefore, is: there may be cases where ProtoBuf and NetSerializer are well-suited to, but in terms of raw performance for at least relatively simple objects... BinaryFormatter is significantly more performant, by at least an order of magnitude.

YMMV.

Jeremy Holovacs
  • 22,480
  • 33
  • 117
  • 254
  • 1
    maybe BinaryFormatter is just really fast with arrays. – Behrooz Oct 28 '17 at 04:03
  • 4
    It's possible... but under the conditions mentioned, the results were dramatic. The lesson here might just be, don't believe one method is the most performant under all circumstances. Testing and benchmarking always enlightens. – Jeremy Holovacs Oct 28 '17 at 14:29
  • In C++ object serialization is about 100 times faster! – Mario Jul 07 '20 at 14:55
  • Very interesting! Everyone claimed protobuf to be the fastest but this clearly shows it's painfully slow. I added my BinaronSerializer to the mix here https://dotnetfiddle.net/gOqQ7p - it's nearly twice as fast as BinaryFormatter, which is already really fast with arrays. – Zach Saw Aug 17 '20 at 01:50
  • @ZachSaw: Speed is not everything. In fact, deserialization speed is usually negligible if the payload is retrieved from a network stream, file or database. In which cases [security](https://github.com/dotnet/runtime/issues/50909) is much more important. Of course, I also created my own [polymorphic serializer](https://github.com/koszeggy/KGySoft.CoreLibraries#binary-serialization) but to be frank a non-polymorphic one that requires annotation will always be more secure. Btw, I forked your fiddle and added my serializer to the group: https://dotnetfiddle.net/K80nIm – György Kőszeg Apr 09 '21 at 12:59
  • @GyörgyKőszeg In this case, however, speed *is* everything, as it is the intent of the original question. – Jeremy Holovacs Apr 18 '21 at 22:39
  • 3
    @JeremyHolovacs: If so, then I should be glad that my serializer is the [fastest one](https://dotnetfiddle.net/K80nIm) in the group. Still, I would formulate more carefully. This is an 11 years old question, and back then security flaws of polymorphic serializers were not taken so seriously. I collected some concrete security issues [in this answer](https://stackoverflow.com/a/67107584/5114784). I focused on `BinaryFormatter` but many of the issues affect other serializers as well. – György Kőszeg Apr 19 '21 at 08:14
16

Yet another serializer out there that claims to be super fast is netserializer.

The data given on their site shows performance of 2x - 4x over protobuf, I have not tried this myself, but if you are evaluating various options, try this as well

Binoj Antony
  • 15,886
  • 25
  • 88
  • 96
  • 3
    I just tried NetSerializer in my application and it works wonders. It is worth a try. – Galen Jun 19 '15 at 17:54
  • netserializer isn't suitable for serializing "user" objects where the library doesn't know what the types are to begin with, or even have the option to force the user to mark their objects as serializable. – Zach Saw Aug 27 '20 at 02:20
16

Protobuf is very very fast.

See http://code.google.com/p/protobuf-net/wiki/Performance for in depth information concerning the performance of this system, and an implementation.

Pieter van Ginkel
  • 29,160
  • 8
  • 71
  • 111
  • Are there any drawbacks to using Protobuf? – Robert Jeppesen Nov 10 '10 at 10:55
  • 12
    You have to annotate your objects. Protobuf does not store the field names and types as the serializers do, but take them from your actual types. This is one of the reasons the target files are much smaller. The documentation explains all of this. I've been using it for some time now, and if you need fast (de)serialization and small target files, protobuf really is the way to go. – Pieter van Ginkel Nov 10 '10 at 11:03
  • Any full source code sample using Protobut in C# for add to the answer? – Kiquenet Feb 15 '13 at 11:59
  • It's not that fast... In fact, it's pretty slow compared to very very very very fast serializers: https://dotnetfiddle.net/gOqQ7p – Zach Saw Aug 27 '20 at 01:22
  • @ZachSaw it's not as fast if you're just dealing with arrays of integers (your example), but very few people are only serializing integers. You see the speed benefits (or at least I do), when you start to deal with nested complex types with lots of members. – matt.rothmeyer Oct 25 '20 at 20:13
6

The binary serializer included with .net should be faster that the XmlSerializer. Or another serializer for protobuf, json, ...

But for some of them you need to add Attributes, or some other way to add metadata. For example ProtoBuf uses numeric property IDs internally, and the mapping needs to be somehow conserved by a different mechanism. Versioning isn't trivial with any serializer.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
4

I removed the bugs in above code and got below results: Also I am unsure given how NetSerializer requires you to register the types you are serializing, what kind of compatibility or performance differences that could potentially make.

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.

Modified Code

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;
                }
            }
        }
    }
}
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
1

You can try Salar.Bois serializer which has a decent performance. Its focus is on payload size but it also offers good performance.

There are benchmarks in the Github page if you wish to see and compare the results by yourself.

https://github.com/salarcode/Bois

Salar
  • 2,088
  • 21
  • 26
0

I took the liberty of feeding your classes into the CGbR generator. Because it is in an early stage it doesn't support DateTime yet, so I simply replaced it with long. The generated serialization code looks like this:

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

I created a list of sample objects like 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);
}

Results on my machine in Release build:

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

Size: 149000 bytes

Time: 2.059ms 3.13ms

Edit: Starting with CGbR 0.4.3 the binary serializer supports DateTime. Unfortunately the DateTime.ToBinary method is insanely slow. I will replace it with somehting faster soon.

Edit2: When using UTC DateTime by invoking ToUniversalTime() the performance is restored and clocks in at 1.669ms.

Toxantron
  • 2,218
  • 12
  • 23