Najszybszy sposób serializacji i deserializacji obiektów .NET

87

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);
    }        
}
aron
źródło
2
Wydajność czy odcisk stopy kodu?
ulrichb
Pytasz mnie, czy potrzebuję danych dotyczących wydajności lub kodu?
aron
3
Pyta, czy mówiąc „najszybszy sposób” masz na myśli wydajność, czy też ślad kodu. BinaryFormatterjest niezwykle szybki pod względem kodu i implementacji, ale rozwiązanie takie jak Marc będzie działać szybciej w benchmarku.
Cody Gray
ok, rozumiem, miałem na myśli wydajność ...
aron
Istnieje wiele linków. Jeden taki: blogs.msdn.com/b/youssefm/archive/2009/07/10/…
nawfal

Odpowiedzi:

57

Oto Twój model (z wynalezionym CTi TE) wykorzystującym protobuf-net (ale zachowujący możliwość używania XmlSerializer, 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);
        }
    }
}
Marc Gravell
źródło
2
Dzień dobry Marc, uwielbiam pracę nad buforami protokołów, którą wykonałeś i wiem, że ten post ma prawie 5 lat, ale netserializer cytowany w odpowiedzi tutaj (Binoj) ma wskaźniki wskazujące, że twoja implementacja nie jest najszybsza. Czy to uczciwe oświadczenie / reklama, czy też istnieje kompromis? dzięki
Jeremy Thompson
ok Teraz widzę, NetSerialization działa tylko dla tej samej wersji, w której szukam Version Tolerant Serialization
Jeremy Thompson
1
Każdy, kto uważa, że ​​to jest szybkie, musi coś palić, może to być wystarczająco szybkie dla wielu przypadków i może być szybsze niż wiele innych serializacji, ale czy jest to rzeczywiście szybkie w porównaniu z ręcznym analizowaniem? Mój Boże nie.
BjarkeCK
Serializatory @BjarkeCK są z natury nieco bardziej zaangażowane, ponieważ muszą zrobić wiele rzeczy, aby ludzie nie odstrzelili sobie nóg (szczególnie, gdy iterują wersje); większość ludzi nie chce spędzać życia na debugowaniu kodu serializacji, więc: dobry serializator - choć niewątpliwie wolniejszy niż doskonale zaimplementowana, nietolerująca wersji ręczna implementacja - jest zwykle dobrym kompromisem dla większości ludzi
Marc Gravell
1
@BjarkeCK Zdecydowanie się nie zgadzam; że nie jest nawet zdalnie przydatne dla większości ludzi. Co dalej - codziennie pisać własne kolekcje? Nie: robienie tego nawet w miarę dobrze jest trudne . Jasne, jeśli naprawdę potrzebujesz najszybszego wydruku: będziesz musiał ubrudzić sobie ręce - ale dla większości ludzi byłoby to naprawdę stratą czasu. W NAJLEPSZYM zajęłoby im to znacznie więcej czasu. Bardziej prawdopodobne jest, że ich kod byłby błędny, zawodny i prawdopodobnie wolniejszy niż przy użyciu dostępnych bibliotek. Większość ludzi powinna skoncentrować się na tym, czego potrzebuje ich aplikacja , a nie na tych drobiazgach.
Marc Gravell
33

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 wprowadź opis obrazu tutaj

Maksyma
źródło
5
To nie jest prędkość. To powolność. W powiązanym artykule jest napisane „mniejsze znaczy lepsze”.
Timur Nuriyasov
2
@TimurNuriyasov, tyle czasu zajęło wykonanie operacji
Maxim
2
Więc mówisz, że binarny jest najwolniejszy? Nie sądzę! Myślę, że poprawnie odnosi się do szybkości, a nie czasu.
Javid
2
Binarny JEST najwolniejszy. Spróbuj sam. Ale powiedziałbym, że jest to najłatwiejsze, ponieważ nie wymaga żadnych niestandardowych rozwiązań do poprawnej pracy z obiektami polimorficznymi (interfejsami itp.)
Kamarey
1
@Kamarey spójrz na mój test poniżej ... binarny jest znacznie szybszy niż inne.
Jeremy Holovacs
19

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.

Jeremy Holovacs
źródło
1
może BinaryFormatter jest po prostu naprawdę szybki w przypadku tablic.
Behrooz
4
To możliwe ... ale w wymienionych warunkach wyniki były dramatyczne. Lekcja może po prostu brzmieć: nie wierz, że jedna metoda jest najskuteczniejsza w każdych okolicznościach. Testowanie i testy porównawcze zawsze wyjaśniają.
Jeremy Holovacs
W C ++ serializacja obiektów jest około 100 razy szybsza!
Mario M
Bardzo interesujące! Wszyscy twierdzili, że protobuf był najszybszy, ale to wyraźnie pokazuje, że jest boleśnie wolny. Dodałem mój BinaronSerializer do miksu tutaj dotnetfiddle.net/gOqQ7p - jest prawie dwa razy szybszy niż BinaryFormatter, który jest już naprawdę szybki z tablicami.
Zach Saw
16

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.

Pieter van Ginkel
źródło
Czy są jakieś wady korzystania z Protobuf?
Robert Jeppesen
11
Musisz opisywać swoje obiekty. Protobuf nie przechowuje nazw i typów pól tak, jak robią to serializatory, ale pobiera je z rzeczywistych typów. Jest to jeden z powodów, dla których pliki docelowe są znacznie mniejsze. Dokumentacja wyjaśnia to wszystko. Używam go już od jakiegoś czasu i jeśli potrzebujesz szybkiej (de) serializacji i małych plików docelowych, protobuf naprawdę jest do zrobienia.
Pieter van Ginkel
Jakikolwiek pełny przykład kodu źródłowego używający Protobut w C # do dodania do odpowiedzi?
Kiquenet
Nie jest tak szybko ... W rzeczywistości jest dość powolny w porównaniu do bardzo, bardzo, bardzo szybkich serializatorów: dotnetfiddle.net/gOqQ7p
Zach Saw
@ZachSaw nie jest tak szybkie, jeśli masz do czynienia tylko z tablicami liczb całkowitych (Twój przykład), ale bardzo niewiele osób serializuje tylko liczby całkowite. Widzisz korzyści związane z szybkością (a przynajmniej ja), gdy zaczynasz zajmować się zagnieżdżonymi typami złożonymi z wieloma członkami.
matt.rothmeyer
15

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

Binoj Antony
źródło
3
Właśnie wypróbowałem NetSerializer w mojej aplikacji i działa cuda. Warte spróbowania.
Galen
netserializer nie nadaje się do serializacji obiektów „użytkowników”, w przypadku których biblioteka nie wie, od czego mają zacząć się typy, ani nawet nie ma opcji wymuszenia na użytkowniku oznaczenia obiektów jako możliwych do serializacji.
Zach Saw
6

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.

CodesInChaos
źródło
Tak, jest rzeczywiście bardzo szybki i obsługuje o wiele więcej przypadków / typów niż Xml.
leppie
1

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;
                }
            }
        }
    }
}
John Heilman
źródło
1
Jakie błędy masz na myśli?
Jeremy Holovacs
0

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

Salar
źródło
0

Pozwoliłem sobie nakarmić twoje klasy w Pozwoliłem generatora CGbR .Ponieważ jest na wczesnym etapie, nie obsługuje DateTime jeszcze , więc po prostu zastąpiłem go długim. Wygenerowany kod serializacji wygląda następująco:

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 Releasekompilacji:

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

Rozmiar: 149000 bajtów

Czas: 2,059 ms 3,13 ms

Edycja: począwszy od CGbR 0.4.3 serializator binarny obsługuje datę i godzinę. Niestety DateTime.ToBinarymetoda jest niesamowicie powolna. Wkrótce zastąpię go czymś szybszym.

Edycja2: W przypadku korzystania z UTC DateTimeprzez wywołanie ToUniversalTime()wydajności jest przywracana i zegary na 1,669 ms .

Toxantron
źródło