2010-08-18 19 views
6

Possiedo un insieme molto grande di file binari in cui diverse migliaia di frame grezzi di video vengono letti e elaborati in sequenza, e ora sto cercando di ottimizzarlo in quanto sembra essere più legato alla CPU di I/O-bound.Prestazioni di lettura file binari .NET

I telai sono attualmente in fase di lettura in questo modo, e ho il sospetto questo è il più grande colpevole:

private byte[] frameBuf; 
BinaryReader binRead = new BinaryReader(FS); 

// Initialize a new buffer of sizeof(frame) 
frameBuf = new byte[VARIABLE_BUFFER_SIZE]; 
//Read sizeof(frame) bytes from the file 
frameBuf = binRead.ReadBytes(VARIABLE_BUFFER_SIZE); 

farebbe molta differenza in .NET per ri-organizzare l'I/O di evitare creando tutti questi nuovi array di byte con ogni frame?

La mia comprensione del meccanismo di allocazione della memoria di .NET è debole poiché provengo da uno sfondo puro C/C++. La mia idea è di riscrivere questo per condividere una classe di buffer statico che contiene un buffer condiviso molto grande con un intero che tiene traccia delle dimensioni effettive del frame, ma adoro la semplicità e la leggibilità dell'attuale implementazione e preferisco tenerlo se il CLR lo gestisce già in un modo che non conosco.

Qualsiasi input sarebbe molto apprezzato.

+5

Hai eseguito un profiler per garantire che il rendimento non provenga da altre fonti? O sei appena andato e hai pensato "Quindi è probabile?" –

+0

Ciao David, Ho eseguito il profiler delle prestazioni su di esso alcune volte e questo particolare metodo è il più costoso. Pertanto, sto cercando di vedere se questo metodo "new byte []" è un evidente killer delle prestazioni in .NET. Come programmatore C, questo sembra analogo a migliaia di istruzioni "malloc" per ciascun buffer, che sarebbe sicuramente più lento di un buffer riutilizzato. – rnd

risposta

7

Non è necessario eseguire frameBuf se si utilizza binRead.ReadBytes - si otterrà un nuovo array di byte che sovrascriverà quello appena creato. Questo però crea una nuova matrice per ogni lettura.

Se si desidera evitare la creazione di un gruppo di matrici di byte, è possibile utilizzare binRead.Read, che inserirà i byte in una matrice fornita dall'utente. Se altri thread stanno usando l'array, vedranno comunque il suo contenuto cambiare di fronte a loro. Assicurati di poter garantire che hai finito con il buffer prima di riutilizzarlo.

+0

Grazie per avermelo fatto notare - sono sicuro che la mia allocazione ridondante sta rallentando sensibilmente questo valore.E l'array condiviso statico è esattamente quello che sto considerando, ma se il guadagno di prestazioni non è grande rispetto alla creazione degli array di byte, preferirei molto con l'elegante soluzione per la stessa complicità che descrivi (accesso condiviso) . – rnd

1

È necessario fare attenzione qui. È molto facile ottenere risultati di test fittizi su codice come questo, risultati che non vengono mai riprodotti in modo reale. Il problema è la cache del file system, che memorizzerà nella cache i dati letti da un file. Il problema inizia quando si esegue il test più e più volte, modificando il codice e cercando miglioramenti.

La seconda volta e le volte successive si esegue il test, i dati non vengono più fuori dal disco. È ancora presente nella cache, richiede solo una copia da memoria per accedere al tuo programma. È molto veloce, un microsecondo o più di overhead più il tempo necessario per copiare. Che funziona a velocità di bus, almeno 5 gigabyte al secondo su macchine moderne.

Il test rivelerà ora che trascorri molto tempo ad allocare il buffer e ad elaborare i dati, relativi alla quantità di tempo trascorso nella lettura dei dati.

Questo raramente si ripro in uso reale. I dati non saranno ancora nella cache, ora il disco rigido deve cercare i dati (molti millisecondi) e deve essere letto dal disco (un paio di dozzine di megabyte al secondo, nel migliore dei casi). La lettura dei dati richiede ora più di tre o quattro magnitudini di tempo. Se sei riuscito a fare il passo di elaborazione due volte più velocemente, il tuo programma funzionerà solo dello 0,05% più velocemente. Prendere o lasciare.

+0

Questo è un buon punto, tuttavia sto eseguendo i miei test su un set di dati che nani la memoria della mia macchina di diversi gigabyte. Ciò che mi preoccupa è il fatto che un codice simile nella mia vecchia libreria C++ processerebbe questo set di dati in meno della metà del tempo necessario. Tuttavia, ho notato che il profilo avverte che circa 2.826 pagine per/s vengono scritte su disco e che l'app potrebbe essere legata alla memoria. Non sto eliminando esplicitamente nessuno di questi array - questi potrebbero essere messi in cache prima che il GC li de-assegni loro? – rnd

+2

Questi buffer sono probabilmente grandi, più di 85 KB. Che li ottiene allocati nel LOH. Saranno bloccati lì per un po ', ci vuole una collezione di gen # 2. Niente è gratis, il riutilizzo dei buffer quando sono grandi è una buona strategia anche in .NET. –

+0

Se si desidera forzare il caricamento del file dal disco, cancellare la cache dei file di Windows, come mostrato in questa domanda: http://stackoverflow.com/q/478340/80525 – BKewl