Optimisation de la sérialisation binaire pour les tableaux génériques multidimensionnels

StackOverflow https://stackoverflow.com/questions/223700

  •  03-07-2019
  •  | 
  •  

Question

J'ai une classe que j'ai besoin de sérialiser binaire. La classe contient un champ comme ci-dessous:

private T[,] m_data;

Ces tableaux multidimensionnels peuvent être assez volumineux (des centaines de milliers d'éléments) et de tout type primitif. Lorsque j’ai essayé la sérialisation .net standard sur un objet, le fichier écrit sur le disque était volumineux et je pense que .net stocke de nombreuses données répétées sur les types d’élément et peut-être moins efficacement que possible.

J'ai cherché des sérialiseurs personnalisés, mais je n'en ai pas vu qui traitent des tableaux génériques multidimensionnels. J'ai également expérimenté la compression .net intégrée sur un tableau d'octets du flux de mémoire après une sérialisation réussie, mais pas aussi rapide / compressée que je l'avais espéré.

Ma question est la suivante: devrais-je essayer d'écrire un sérialiseur personnalisé pour sérialiser de manière optimale ce tableau pour le type approprié (cela semble un peu décourageant), ou devrais-je utiliser une sérialisation .net standard et ajouter de la compression?

Tout conseil sur la meilleure approche serait très apprécié, ou des liens vers des ressources montrant comment traiter la sérialisation d’un tableau générique multidimensionnel - comme indiqué exemples existants que j'ai trouvés ne prennent pas en charge de telles structures.

Était-ce utile?

La solution

Voici ce que j'ai proposé. Le code ci-dessous crée un int [1000] [10000] et l'écrit à l'aide de BinaryFormatter dans 2 fichiers - un compressé et l'autre non.

Le fichier compressé est de 1,19 Mo (1 255 339 octets). Décomprimé: 38,2 Mo (40 150 034 octets)

        int width = 1000;
        int height = 10000;
        List<int[]> list = new List<int[]>();
        for (int i = 0; i < height; i++)
        {
            list.Add(Enumerable.Range(0, width).ToArray());
        }
        int[][] bazillionInts = list.ToArray();
        using (FileStream fsZ = new FileStream("c:\\temp_zipped.txt", FileMode.Create))
        using (FileStream fs = new FileStream("c:\\temp_notZipped.txt", FileMode.Create))
        using (GZipStream gz = new GZipStream(fsZ, CompressionMode.Compress))
        {
            BinaryFormatter f = new BinaryFormatter();
            f.Serialize(gz, bazillionInts);
            f.Serialize(fs, bazillionInts);
        }

Je ne peux pas penser à un moyen meilleur / facile de faire cela. La version zippée est très serrée.

Je choisirais BinaryFormatter + GZipStream. Faire quelque chose de personnalisé ne serait pas amusant du tout.

[modifier par MG] J'espère que vous ne serez pas offensé par une modification, mais l'uniforme répété Range (0, width) déforme énormément les choses; changer en:

        int width = 1000;
        int height = 10000;
        Random rand = new Random(123456);
        int[,] bazillionInts = new int[width, height];
        for(int i = 0 ; i < width;i++)
            for (int j = 0; j < height; j++)
            {
                bazillionInts[i, j] = rand.Next(50000);
            }

Et essayez-le; temp_notZipped.txt s'affiche à 40 Mo, temp_zipped.txt à 62 Mo. Pas si attrayant ...

Autres conseils

Le meilleur rapport longueur de code / taille de sortie consisterait à coder votre tableau à l'aide de BitConverter, en convertissant tous les éléments en leur format binaire compact. Je sais qu’il est manuel, mais qu’il économisera de 80 à 90% d’espace par rapport à la sérialisation binaire .NET.

Pouvez-vous définir "grand" et "?" L'exemple 1000x10000xint (un autre article) sort à 40 Mo; et 1000x10000x4 octets (= int) est de 38 Mo. En ce qui concerne les frais généraux, ce n'est pas terrible.

Quel type de données T est susceptible d’être? Juste des primatives? Je pense que je pourrais probablement éditer protobuf-net pour prendre en charge les tableaux rectangulaires < code> * - mais pour conserver une compatibilité filaire, nous aurions probablement besoin d'un en-tête (un octet) par élément - soit 9 Mo de surcharge pour l'exemple 1000x10000.

Cela ne vaut probablement pas la peine pour des choses comme float , double , etc. (car ils sont stockés in extenso sous "protocoles tampons") - mais il peut y avoir des économies pour des choses comme int simplement en raison de la façon dont elles sont compressées ... (surtout si elles ont tendance à être plus petites [magnitude]). Enfin, si T est en réalité un objet comme Personne , etc., il devrait alors être lot meilleur que la sérialisation binaire, car il est très performant pour le conditionnement d'objets.

Il ne serait pas trivial de chausser des cornes dans des tableaux rectangulaires, mais laissez-moi savoir si c'est quelque chose qui vous intéresserait d'essayer.

* : ce n'est pas le cas depuis les "tampons de protocole". spec ne les supporte pas, mais on peut bidouiller ça ...

La raison pour laquelle il faut autant de données sur les types est que votre tableau de T peut être n’importe quel type, mais plus précisément, T peut être du type SomeBaseClass, et vous pouvez toujours stocker SomeDerivedClass dans ce tableau et le désérialiseur. aurait besoin de savoir cela.

Mais ces données redondantes en font un bon candidat pour la compression, comme d'autres l'ont noté.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top