Domanda

Ho una classe di cui ho bisogno per serializzare binariamente. La classe contiene un campo come di seguito:

private T[,] m_data;

Questi array multidimensionali possono essere abbastanza grandi (centinaia di migliaia di elementi) e di qualsiasi tipo primitivo. Quando ho provato la serializzazione .net standard su un oggetto, il file scritto su disco era di grandi dimensioni e penso che .net stia memorizzando molti dati ripetuti sui tipi di elementi e forse non nel modo più efficiente possibile.

Ho cercato serializzatori personalizzati ma non ho visto nessuno che si occupasse di array generici multidimensionali. Ho anche sperimentato la compressione .net integrata su un array di byte del flusso di memoria dopo aver serializzato un certo successo, ma non così veloce / compresso come avevo sperato.

La mia domanda è: dovrei provare a scrivere un serializzatore personalizzato per serializzare in modo ottimale questo array per il tipo appropriato (questo sembra un po 'scoraggiante), o dovrei usare la serializzazione .net standard e aggiungere la compressione?

Qualunque consiglio sull'approccio migliore sarebbe più apprezzato, o collegamenti a risorse che mostrano come affrontare la serializzazione di un array generico multidimensionale - come menzionato esempi esistenti Ho scoperto che non supportano tali strutture.

È stato utile?

Soluzione

Ecco cosa mi è venuto in mente. Il codice seguente crea un int [1000] [10000] e lo scrive usando BinaryFormatter in 2 file: uno compresso e l'altro no.

Il file zippato è 1,19 MB (1.255.339 byte) La decompressione è di 38,2 MB (40.150.034 byte)

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

Non riesco a pensare a un modo migliore / semplice per farlo. La versione zippata è piuttosto dannatamente stretta.

Andrei con BinaryFormatter + GZipStream. Fare qualcosa di personalizzato non sarebbe affatto divertente.


[modifica di MG] Spero che non ti offenda una modifica, ma l'uniforme ripetuta Range (0, larghezza) sta distorcendo enormemente le cose; cambia in:

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

E provalo; vedrai temp_notZipped.txt a 40 MB, temp_zipped.txt a 62 MB. Non così attraente ...

Altri suggerimenti

Il miglior rapporto tra lunghezza del codice e dimensione dell'output sarebbe codificare l'array usando BitConverter, convertendo tutti gli elementi nel loro formato binario compatto. È manuale, lo so, ma risparmierà l'80-90% di spazio rispetto alla serializzazione binaria .NET.

Puoi definire " large " ;? L'esempio 1000x10000xint (un altro post) esce a 40 Mb; e 1000x10000x4 byte (= int) è 38 MB. Per quanto riguarda le spese generali, non è terribile.

Che tipo di dati è probabile che sia? Solo primitivi? Sto pensando che probabilmente potrei modificare protobuf-net per supportare array rettangolari < codice> * - ma per mantenere un qualche tipo di compatibilità dei cavi avremmo probabilmente bisogno di un'intestazione (un byte) per elemento - cioè 9 MB di overhead per l'esempio 1000x10000.

Probabilmente non ne vale la pena per cose come float , double , ecc. (poiché sono archiviati alla lettera sotto "buffer di protocollo") - ma potrebbero esserci risparmi per cose come int semplicemente grazie al modo in cui racchiude ints ... (specialmente se tendono ad essere sul lato più piccolo [magnitudo]). Infine, se T è in realtà oggetti come Person ecc, allora dovrebbe essere un lotto migliore della serializzazione binaria, poiché è molto bravo a impacchettare gli oggetti.

Non sarebbe banale indossare il clacson negli array rettangolari, ma fammi sapere se questo è qualcosa che ti interesserebbe provare.

* : al momento non funziona poiché i buffer di protocollo "quot" le specifiche non li supportano, ma possiamo aggirare il problema ...

Il motivo per cui ci devono essere così tanti dati sui tipi è che la tua matrice di T potrebbe essere di qualsiasi tipo, ma più specificamente, T potrebbe essere del tipo SomeBaseClass e potresti comunque archiviare SomeDerivedClass in quella matrice e il deserializzatore avrebbe bisogno di saperlo.

Ma questi dati ridondanti lo rendono un buon candidato per la compressione, come altri hanno notato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top