95 votes

Moyen le plus rapide de sérialiser et désérialiser les objets .NET

Je cherche le moyen le plus rapide de sérialiser et désérialiser des objets .NET. Voici ce que j'ai jusqu'à présent:

public class TD
{
    public List CTs { get; set; }
    public List 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 tData)
    {
        var serializer = new XmlSerializer(typeof(List));

        TextWriter writer = new StringWriter();
        serializer.Serialize(writer, tData);

        return writer.ToString();
    }

    public static List Deserialize(string tData)
    {
        var serializer = new XmlSerializer(typeof(List));

        TextReader reader = new StringReader(tData);

        return (List)serializer.Deserialize(reader);
    }        
}

2 votes

Performance ou empreinte de code?

0 votes

Est-ce que tu me demandes si j'ai besoin de données de performance ou de code?

3 votes

Il demande si, par "le moyen le plus rapide", vous entendez en termes de performance ou en termes d'empreinte de code. BinaryFormatter est extrêmement rapide en termes de code et d'implémentation, mais une solution comme celle de Marc sera plus performante dans un test de référence.

6voto

CodesInChaos Points 60274

Le sérialiseur binaire inclus dans .net devrait être plus rapide que XmlSerializer. Ou un autre sérialiseur pour protobuf, json, ...

Mais pour certains d'entre eux, vous devez ajouter des attributs, ou une autre manière d'ajouter des métadonnées. Par exemple, ProtoBuf utilise des ID de propriété numériques en interne, et le mappage doit être conservé d'une manière différente. La version n'est pas triviale avec n'importe quel sérialiseur.

0 votes

Oui, c'est en effet très rapide, et il gère beaucoup plus de cas/types que celui en Xml.

4voto

John Heilman Points 61

J'ai supprimé les bugs dans le code ci-dessus et obtenu les résultats ci-dessous : De plus, je ne suis pas sûr de savoir compte tenu du fait que NetSerializer vous demande d'enregistrer les types que vous serializez, quels types de compatibilité ou de différences de performances cela pourrait potentiellement entraîner.

Génération de 100000 tableaux de données...
Données de test générées.
Test du sérialiseur binaire...
BinaryFormatter : La sérialisation a pris 508.9773ms.
BinaryFormatter : La désérialisation a pris 371.8499ms.

Test du sérialiseur ProtoBuf...
ProtoBuf : La sérialisation a pris 3280.9185ms.
ProtoBuf : La désérialisation a pris 3190.7899ms.

Test du sérialiseur NetSerializer...
NetSerializer : La sérialisation a pris 427.1241ms.
NetSerializer : La désérialisation a pris 78.954ms.
Appuyez sur une touche pour terminer.

Code modifié

utiliser System;
utiliser System.Collections.Generic;
utiliser System.Diagnostics;
utiliser System.IO;
utiliser System.Linq;
utiliser System.Runtime.Serialization.Formatters.Binary;
utiliser System.Text;
utiliser System.Threading.Tasks;

espace de noms SerializationTests
{
    classe Programme
    {
        statique vide Main(string[] args)
        {
            var count = 100000;
            var rnd = nouveau Random((int)DateTime.UtcNow.Ticks & 0xFF);
            Console.WriteLine("Génération de {0} tableaux de données...", count);
            var tableaux = nouveau List();
            pour (int i = 0; i < count; i++)
            {
                var elements = rnd.Next(1, 100);
                var tableau = nouveau int[elements];
                pour (int j = 0; j < elements; j++)
                {
                    tableau[j] = rnd.Next();
                }
                tableaux.Add(tableau);
            }
            Console.WriteLine("Données de test générées.");
            var chronomètre = nouveau Stopwatch();

            Console.WriteLine("Test du sérialiseur binaire...");
            var sérialiseurbinaire = nouveau BinarySerializer();
            var binaireSérialisée = nouveau List();
            var binaireDésérialisée = nouveau List();

            chronomètre.Reset();
            chronomètre.Start();
            foreach (var tableau in tableaux)
            {
                binaireSérialisée.Add(sérialiseurbinaire.Serialize(tableau));
            }
            chronomètre.Stop();
            Console.WriteLine("BinaryFormatter : La sérialisation a pris {0}ms.", chronomètre.Elapsed.TotalMilliseconds);

            chronomètre.Reset();
            chronomètre.Start();
            foreach (var sérialisé in binaireSérialisée)
            {
                binaireDésérialisée.Add(sérialiseurbinaire.Deserialize(sérialisé));
            }
            chronomètre.Stop();
            Console.WriteLine("BinaryFormatter : La désérialisation a pris {0}ms.", chronomètre.Elapsed.TotalMilliseconds);

            Console.WriteLine();
            Console.WriteLine("Test du sérialiseur ProtoBuf...");
            var sérialiseurprotobuf = nouveau ProtoBufSerializer();
            var protobufSérialisée = nouveau List();
            var protobufDésérialisée = nouveau List();

            chronomètre.Reset();
            chronomètre.Start();
            foreach (var tableau in tableaux)
            {
                protobufSérialisée.Add(sérialiseurprotobuf.Serialize(tableau));
            }
            chronomètre.Stop();
            Console.WriteLine("ProtoBuf : La sérialisation a pris {0}ms.", chronomètre.Elapsed.TotalMilliseconds);

            chronomètre.Reset();
            chronomètre.Start();
            foreach (var sérialisé in protobufSérialisée)
            {
                protobufDésérialisée.Add(sérialiseurprotobuf.Deserialize(sérialisé));
            }
            chronomètre.Stop();
            Console.WriteLine("ProtoBuf : La désérialisation a pris {0}ms.", chronomètre.Elapsed.TotalMilliseconds);

            Console.WriteLine();
            Console.WriteLine("Test du sérialiseur NetSerializer...");
            var netSerializerSérialisée = nouveau List();
            var netSerializerDésérialisée = nouveau List();

            chronomètre.Reset();
            chronomètre.Start();
            var netSerializerSérialiseur = nouveau NS();
            foreach (var tableau in tableaux)
            {
                netSerializerSérialisée.Add(netSerializerSérialiseur.Serialize(tableau));
            }
            chronomètre.Stop();
            Console.WriteLine("NetSerializer : La sérialisation a pris {0}ms.", chronomètre.Elapsed.TotalMilliseconds);

            chronomètre.Reset();
            chronomètre.Start();
            foreach (var sérialisé in netSerializerSérialisée)
            {
                netSerializerDésérialisée.Add(netSerializerSérialiseur.Deserialize(sérialisé));
            }
            chronomètre.Stop();
            Console.WriteLine("NetSerializer : La désérialisation a pris {0}ms.", chronomètre.Elapsed.TotalMilliseconds);

            Console.WriteLine("Appuyez sur une touche pour terminer.");
            Console.ReadKey();
        }

        public class BinarySerializer
        {
            privé statique readonly BinaryFormatter Formatter = nouveau BinaryFormatter();

            public byte[] Serialize(object àSérialiser)
            {
                utiliser (var stream = nouveau MemoryStream())
                {
                    Formatter.Serialize(stream, àSérialiser);
                    return stream.ToArray();
                }
            }

            public T Deserialize(byte[] sérialisé)
            {
                utiliser (var stream = nouveau MemoryStream(sérialisé))
                {
                    var résultat = (T)Formatter.Deserialize(stream);
                    return résultat;
                }
            }
        }

        public class ProtoBufSerializer
        {
            public byte[] Serialize(object àSérialiser)
            {
                utiliser (var stream = nouveau MemoryStream())
                {
                    ProtoBuf.Serializer.Serialize(stream, àSérialiser);
                    return stream.ToArray();
                }
            }

            public T Deserialize(byte[] sérialisé)
            {
                utiliser (var stream = nouveau MemoryStream(sérialisé))
                {
                    var résultat = ProtoBuf.Serializer.Deserialize(stream);
                    return résultat;
                }
            }
        }

        public class NS
        {
            NetSerializer.Serializer Serializer = nouveau NetSerializer.Serializer(new Type[] { typeof(int), typeof(int[]) });

            public byte[] Serialize(object àSérialiser)
            {
                utiliser (var stream = nouveau MemoryStream())
                {
                    Serializer.Serialize(stream, àSérialiser);
                    return stream.ToArray();
                }
            }

            public T Deserialize(byte[] sérialisé)
            {
                utiliser (var stream = nouveau MemoryStream(sérialisé))
                {
                    Serializer.Deserialize(stream, out var résultat);
                    return (T)résultat;
                }
            }
        }
    }
}

1 votes

Quels bogues faites-vous référence?

0voto

Salar Khalilzadeh Points 655

Vous pouvez essayer le sérialiseur Salar.Bois qui offre des performances décentes. Son objectif est la taille de la charge utile mais il offre également de bonnes performances.

Il y a des benchmarks sur la page Github si vous souhaitez voir et comparer les résultats par vous-même.

https://github.com/salarcode/Bois

0voto

Toxantron Points 1625

J'ai pris la liberté de convertir vos classes dans le générateur CGbR. Comme il est dans une phase précoce, il ne prend pas encore en charge DateTime, j'ai donc simplement remplacé par long. Le code de sérialisation généré ressemble à ceci :

public int Taille
{
    get 
    { 
        var taille = 24;
        // Ajouter la taille pour les collections et les chaînes de caractères
        taille += Cts == null ? 0 : Cts.Count * 4;
        taille += Tes == null ? 0 : Tes.Count * 4;
        taille += Code == null ? 0 : Code.Length;
        taille += Message == null ? 0 : Message.Length;

        return taille;              
    }
}

public byte[] EnOctets(byte[] octets, ref int index)
{
    if (index + Taille > octets.Length)
        throw new ArgumentOutOfRangeException("index", "L'objet ne rentre pas dans le tableau");

    // Convertir Cts
    // Deux informations de longueur en octets pour chaque dimension
    GeneratorByteConverter.Include((ushort)(Cts == null ? 0 : Cts.Count), octets, ref index);
    if (Cts != null)
    {
        for(var i = 0; i < Cts.Count; i++)
        {
            var valeur = Cts[i];
            valeur.EnOctets(octets, ref index);
        }
    }
    // Convertir Tes
    // Deux informations de longueur en octets pour chaque dimension
    GeneratorByteConverter.Include((ushort)(Tes == null ? 0 : Tes.Count), octets, ref index);
    if (Tes != null)
    {
        for(var i = 0; i < Tes.Count; i++)
        {
            var valeur = Tes[i];
            valeur.EnOctets(octets, ref index);
        }
    }
    // Convertir Code
    GeneratorByteConverter.Include(Code, octets, ref index);
    // Convertir Message
    GeneratorByteConverter.Include(Message, octets, ref index);
    // Convertir Date de début
    GeneratorByteConverter.Include(StartDate.ToBinary(), octets, ref index);
    // Convertir Date de fin
    GeneratorByteConverter.Include(EndDate.ToBinary(), octets, ref index);
    return octets;
}

public Td ÀPartirDesOctets(byte[] octets, ref int index)
{
    // Lire Cts
    var longueurCts = GeneratorByteConverter.ToUInt16(octets, ref index);
    var tempCts = new List(longueurCts);
    for (var i = 0; i < longueurCts; i++)
    {
        var valeur = new Ct().ÀPartirDesOctets(octets, ref index);
        tempCts.Add(valeur);
    }
    Cts = tempCts;
    // Lire Tes
    var longueurTes = GeneratorByteConverter.ToUInt16(octets, ref index);
    var tempTes = new List(longueurTes);
    for (var i = 0; i < longueurTes; i++)
    {
        var valeur = new Te().ÀPartirDesOctets(octets, ref index);
        tempTes.Add(valeur);
    }
    Tes = tempTes;
    // Lire Code
    Code = GeneratorByteConverter.GetString(octets, ref index);
    // Lire Message
    Message = GeneratorByteConverter.GetString(octets, ref index);
    // Lire Date de début
    StartDate = DateTime.FromBinary(GeneratorByteConverter.ToInt64(octets, ref index));
    // Lire Date de fin
    EndDate = DateTime.FromBinary(GeneratorByteConverter.ToInt64(octets, ref index));

    return this;
}

J'ai créé une liste d'objets d'exemple comme suit :

var objets = new List();
for (int i = 0; i < 1000; i++)
{
    var obj = new Td
    {
        Message = "Bonjour mon ami",
        Code = "Un code qui peut être ajouté ici",
        StartDate = DateTime.Now.AddDays(-7),
        EndDate = DateTime.Now.AddDays(2),
        Cts = new List(),
        Tes = new List()
    };
    for (int j = 0; j < 10; j++)
    {
        obj.Cts.Add(new Ct { Foo = i * j });
        obj.Tes.Add(new Te { Bar = i + j });
    }
    objets.Add(obj);
}

Résultats sur mon ordinateur en mode Release :

var montre = new Stopwatch();
montre.Start();
var octets = BinarySerializer.SerializeMany(objets);
montre.Stop();

Taille : 149000 octets

Temps : 2.059ms 3.13ms

Édition : À partir de CGbR 0.4.3, le sérialiseur binaire prend en charge DateTime. Malheureusement, la méthode DateTime.ToBinary est incroyablement lente. Je vais la remplacer bientôt par quelque chose de plus rapide.

Édition2 : Lorsque l'on utilise UTC DateTime en invoquant ToUniversalTime(), les performances sont restaurées et s'établissent à 1.669ms.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X