2011-01-26 1 views
9

Ho un'applicazione .NET scritta in C# (.NET 4.0). In questa applicazione, dobbiamo leggere un set di dati di grandi dimensioni da un file e visualizzare i contenuti in una struttura a griglia. Quindi, per fare ciò, ho inserito un DataGridView nel modulo. Ha 3 colonne, tutti i dati delle colonne provengono dal file. Inizialmente, il file aveva circa 600.000 record, corrispondenti a 600.000 righe nel DataGridView.Gestione di serie di dati molto grandi e caricamento just in time

Ho subito scoperto che, DataGridView collassa con un set di dati così grande, così ho dovuto passare alla modalità virtuale. Per fare ciò, prima leggo il file completamente in 3 diversi array (corrispondenti a 3 colonne), quindi viene generato l'evento CellValueNeeded, fornisco i valori corretti dagli array.

Tuttavia, in questo file può esserci un numero enorme (ENORME!) Di record, come abbiamo rapidamente scoperto. Quando la dimensione del record è molto grande, la lettura di tutti i dati in un array o un elenco <>, ecc. Sembra non essere fattibile. Corriamo rapidamente negli errori di allocazione della memoria. (Eccezione di memoria insufficiente).

Siamo rimasti bloccati lì, ma poi ci siamo resi conto, perché leggere prima tutti i dati negli array, perché non leggere il file su richiesta con l'evento CellValueNeeded? Ecco cosa facciamo ora: apriamo il file, ma non leggiamo nulla e, quando gli eventi CellValueNeeded si attivano, cerchiamo dapprima Seek() nella posizione corretta nel file, quindi leggiamo i dati corrispondenti.

Questo è il meglio che abbiamo potuto trovare, ma, prima di tutto, è piuttosto lento, il che rende l'applicazione lenta e non facile da usare. In secondo luogo, non possiamo fare a meno di pensare che ci deve essere un modo migliore per farlo. Ad esempio, alcuni editor binari (come HXD) sono incredibilmente veloci per qualsiasi dimensione di file, quindi mi piacerebbe sapere come questo può essere raggiunto.

Oh, e per aggiungere ai nostri problemi, in modalità virtuale di DataGridView, quando impostiamo RowCount sul numero di file disponibili nel file (diciamo 16.000.000), ci vuole del tempo per DataGridView inizializza da solo. Sarebbe apprezzato anche qualsiasi commento per questo "problema".

Grazie

risposta

5

Se non è possibile montare l'intero set di dati in memoria, allora avete bisogno di un sistema di buffering. Piuttosto che leggere solo la quantità di dati necessari per riempire lo DataGridView in risposta a CellValueNeeded, la tua applicazione dovrebbe anticipare le azioni dell'utente e leggere in anticipo. Quindi, ad esempio, quando il programma si avvia per la prima volta, dovrebbe leggere i primi 10.000 record (o forse solo 1.000 o forse 100.000 - qualsiasi cosa sia ragionevole nel tuo caso). Quindi, le richieste CellValueNeeded possono essere compilate immediatamente dalla memoria.

Mentre l'utente si muove attraverso la griglia, il programma il più possibile rimane un passo avanti all'utente. Ci potrebbero essere brevi pause se l'utente salta davanti a te (per esempio, vuole saltare fino alla fine dal fronte) e devi andare su disco per soddisfare una richiesta.

Questo buffer di solito viene eseguito meglio da un thread separato, anche se la sincronizzazione può a volte essere un problema se il thread sta leggendo in anticipo in attesa dell'azione successiva dell'utente, e quindi l'utente fa qualcosa di completamente inaspettato come saltare all'inizio di la lista.

16 milioni di record non sono in realtà tutti quei record da conservare in memoria, a meno che i record non siano molto grandi. O se non hai molta memoria sul tuo server. Certamente, 16 milioni non sono affatto vicini alla dimensione massima di un List<T>, a meno che lo T sia un tipo di valore (struttura). Di quanti gigabyte di dati stai parlando qui?

+1

Ciao Jim, T, è una struttura con 4 galleggianti a doppia precisione. Quindi, 4 * 8 * 16M = 512 MB di dati. – SomethingBetter

+0

Ho provato a utilizzare .NET MemoryMappedFile, ma non appena si crea una vista, apparentemente tenta di caricare il file in memoria, perché ho esaurito le eccezioni di memoria. Ho pensato che forse MemoryMappedFile avrebbe segmentato internamente i dati accedendo alle pagine e caricato solo le pagine richieste in memoria. – SomethingBetter

+0

@SomethingBetter: Credo che 512 MB sia un problema se si è su una macchina a 32 bit. Se si utilizza un file mappato in memoria, si vorrà rendere la visualizzazione nel file più piccola dell'intera dimensione del file. Quindi modifichi la visualizzazione come pagine utente attraverso i dati. –

1

La gestione di righe e colonne che possono essere arrotolate, subdotate, utilizzate in calcoli a più colonne, ecc. Presenta una serie unica di sfide; non è giusto confrontare il problema con quelli che un editore avrebbe incontrato. I controlli DataGrid di terze parti hanno affrontato il problema della visualizzazione e della manipolazione di dataset di grandi dimensioni lato client dal VB6 giorni. Non è un compito banale ottenere prestazioni davvero allettanti utilizzando i set di dati garguantuan lato client su richiesta o autonomi. Il carico su richiesta può risentire della latenza sul lato server; manipolare l'intero set di dati sul client può soffrire di limiti di memoria e CPU. Alcuni controlli di terze parti che supportano il caricamento just-in-time forniscono sia la logica lato client che lato server, mentre altri tentano di risolvere il problema al 100% lato client.

3

Bene, ecco una soluzione che sembra funzionare molto meglio:

Fase 0: Impostare dataGridView.RowCount su un valore basso, diciamo 25 (o il numero effettivo che si inserisce nel modulo/schermo)

Passaggio 1: disabilitare la barra di scorrimento di dataGridView.

Passaggio 2: aggiungi la tua barra di scorrimento.

Fase 3: nella vostra routine CellValueNeeded, rispondere alle e.RowIndex + scrollBar.Value

Fase 4: Per quanto riguarda il datastore, ho aperto un flusso, e nella routine CellValueNeeded, prima di fare un Seek () e leggi() i dati richiesti.

Con questi passaggi, ottengo prestazioni molto ragionevoli scorrendo il dataGrid per file molto grandi (testati fino a 0,8 GB).

Quindi, in conclusione, sembra che la vera causa del rallentamento non sia il fatto che abbiamo mantenuto Seek() ing e Read() ing, ma lo stesso dataGridView.

+0

È vero. La visualizzazione dello stesso DataSet in un TextBox (con l'aiuto di StringBuilder (5000000);))) è circa 4 volte più veloce. – TomeeNS

0

Per risolvere questo problema, suggerisco di non caricare tutti i dati contemporaneamente. Caricare invece i dati in blocchi e visualizzare i dati più rilevanti quando necessario. Ho appena fatto un test rapido e ho scoperto che l'impostazione di una proprietà DataSource di un DataGridView è un buon approccio, ma con un numero elevato di righe richiede anche del tempo. Quindi utilizzare la funzione Merge di DataTable per caricare i dati in blocchi e mostrare all'utente i dati più rilevanti. Here ho dimostrato un esempio che può aiutarti.