2011-02-02 47 views
9

Sto lavorando a un programma che esegue pesanti accessi in lettura/scrittura su file enormi (fino a 64 GB). I file sono strutturati in modo specifico e per accedervi ho creato un framework; dopo un po 'ho provato a testare le prestazioni su di esso e ho notato che le operazioni di scrittura sequenziali di file preallocato sono troppo lente per essere accettabili. Dopo molti test ho replicato il comportamento senza il mio framework (solo i metodi FileStream); qui è la parte di codice che (con il mio hardware) replica il problema:Strano comportamento con FileStream.WriteFile

FileStream fs = new FileStream("test1.vhd", FileMode.Open); 
byte[] buffer = new byte[256 * 1024]; 
Random rand = new Random(); 
rand.NextBytes(buffer); 
DateTime start, end; 
double ellapsed = 0.0; 
long startPos, endPos; 

BinaryReader br = new BinaryReader(fs); 
br.ReadUInt32(); 
br.ReadUInt32(); 
for (int i = 0; i < 65536; i++) 
    br.ReadUInt16(); 

br = null; 

startPos = 0; // 0 
endPos = 4294967296; // 4GB 
for (long index = startPos; index < endPos; index += buffer.Length) 
{ 
    start = DateTime.Now; 
    fs.Write(buffer, 0, buffer.Length); 
    end = DateTime.Now; 
    ellapsed += (end - start).TotalMilliseconds; 
} 

Purtroppo il problema sembra essere imprevedibile, così a volte "funziona", a volte non è così. Tuttavia, utilizzando Process Monitor ho preso i seguenti eventi:

 
Operation Result Detail 
WriteFile SUCCESS Offset: 1.905.655.816, Length: 262.144 
WriteFile SUCCESS Offset: 1.905.917.960, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.180.104, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.442.248, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.704.392, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.966.536, Length: 262.144 
ReadFile SUCCESS Offset: 1.907.228.672, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
WriteFile SUCCESS Offset: 1.907.228.680, Length: 262.144 
ReadFile SUCCESS Offset: 1.907.355.648, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
ReadFile SUCCESS Offset: 1.907.490.816, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
WriteFile SUCCESS Offset: 1.907.490.824, Length: 262.144 
ReadFile SUCCESS Offset: 1.907.617.792, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
ReadFile SUCCESS Offset: 1.907.752.960, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
WriteFile SUCCESS Offset: 1.907.752.968, Length: 262.144 

Cioè, dopo oltre-scrittura quasi 2 GB, FileStream.Write inizia a chiamare ReadFile dopo ogni WriteFile, e questo numero continuerà fino alla fine del processi; inoltre, l'offset a cui inizia il problema sembra essere casuale. Ho eseguito il debug passo dopo passo all'interno del metodo FileStream.Write e ho verificato che sia effettivamente lo WriteFile (API Win32) che, internamente, chiama ReadFile.

Ultima nota; Non penso che sia un problema di frammentazione dei file: ho deframmentato il file personalmente con contig!

+1

consideri memoria commutazione [file mappati] (http://msdn.microsoft.com/en-us/library/dd997372.aspx). – gor

+0

Vuoi dire che dovrei creare riferimenti da API Win32 o utilizzare .NET4? Nel primo caso, sarà meglio creare l'intero framework in C/C++ (e sto davvero considerando questa possibilità!); nel secondo dovrei anche aggiornare a VS2010 o usare SharpDevelop: preferisco usare quello che ho! – Atropo

+0

Potrebbe trattarsi di un problema di buffering del sistema operativo, non riesco a replicare le letture su Win7 x64 e .Net 4.0. (Inoltre, per favore usa i blocchi 'using', non voglio piangere oggi) – user7116

risposta

1

Credo che questo abbia a che fare con FileStream.Write/Read e con un limite di 2 GB. Stai facendo questo in un processo a 32 bit? Non sono riuscito a trovare alcuna documentazione specifica su questo, ma qui c'è una domanda MSDN forum che suona uguale. Potresti provare a farlo in un processo a 64 bit.

Sono d'accordo tuttavia che l'uso di un file mappato in memoria potrebbe essere un approccio migliore.

+0

Sono in un sistema Win7 a 64 bit! Tuttavia non penso che sia un problema di FileStream.Write: l'ho debugato (dopo una decompilazione di mscorlib)! – Atropo

+0

Questo è molto possibile. Mi risulta che .NET sia ancora limitato a processi a 32 bit o al limite di memoria di 2 GB. Ma non stai allocando più di 2 GB, quindi dubito che questo sia il problema. –

+0

l'app .net ha come target "Qualsiasi CPU" o x86? –

1

L'ho trovato da MSDN. Potrebbe essere correlato? Mi sembra che ogni file abbia un puntatore globalmente condiviso.

Quando un oggetto FileStream non ha una presa esclusiva sul proprio handle, un altro thread può accedere all'handle del file contemporaneamente e modificare la posizione del puntatore del file del sistema operativo associato all'handle del file. In questo caso, la posizione memorizzata nella cache nell'oggetto FileStream e i dati memorizzati nella cache nel buffer potrebbero essere compromessi. L'oggetto FileStream esegue regolarmente controlli sui metodi che accedono al buffer memorizzato nella cache per assicurare che la posizione della maniglia del sistema operativo sia la stessa della posizione memorizzata nella cache utilizzata dall'oggetto FileStream.

http://msdn.microsoft.com/en-us/library/system.io.filestream.aspx

+0

Secondo la documentazione, sembra essere sufficiente usare 'FileOptions.WriteThrough' per disabilitare ogni cache tra' FileStream.Write' e il disco; ma osservo ancora la presenza di 'ReadFile' durante i test. – Atropo