2011-02-02 23 views
9

Pracuję nad programem, który ma duży dostęp do odczytu/zapisu na dużym pliku (do 64 GB). Pliki są ustrukturyzowane i aby uzyskać do nich dostęp, stworzyłem framework; po pewnym czasie próbowałem przetestować na nim wydajność i zauważyłem, że w przypadku wcześniej przydzielonych plików sekwencyjne operacje zapisu są zbyt wolne, aby można je było zaakceptować. Po wielu testach replikowałem zachowanie bez mojego frameworka (tylko metody FileStream); oto fragment kodu (z mojego sprzętu) replikuje problem:Dziwne zachowanie z 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; 
} 

Niestety problem wydaje się być nieprzewidywalne, więc czasami to „działa”, czasami nie. Jednak użycie Process Monitor Złapałem następujące zdarzenia:

 
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 

Oznacza to, że po ponad pisanie prawie 2 GB FileStream.Write rozpoczyna zadzwonić ReadFile po każdym WriteFile i ten problem w dalszym ciągu aż do końca proces; również przesunięcie, od którego zaczyna się problem, wydaje się przypadkowe. Przeprowadziłem debugowanie krok po kroku wewnątrz metody FileStream.Write i sprawdziłem, czy faktycznie jest to WriteFile (Win32 API), który wewnętrznie wywołuje ReadFile.

Ostatnia uwaga; Nie sądzę, że jest to problem fragmentacji plików: osobiście zdefragmentowałem plik za pomocą contig!

+1

Rozważyć pamięci przełączania [odwzorowanych Files] (http://msdn.microsoft.com/en-us/library/dd997372.aspx). – gor

+0

Masz na myśli, że powinienem utworzyć referencje z Win32 API lub korzystać z .NET4? W pierwszym przypadku lepiej będzie stworzyć całe środowisko w C/C++ (i naprawdę rozważam taką możliwość!); w tym ostatnim powinienem również uaktualnić do VS2010 lub użyć SharpDevelop: wolę używać tego, co mam! – Atropo

+0

Może to być problem z buforowaniem systemu operacyjnego, nie mogę replikować odczytów na Win7 x64 i .Net 4.0. (Użyj też bloków 'using', nie chcę dzisiaj płakać) – user7116

Odpowiedz

1

Uważam, że ma to związek z FileStream.Write/Read i limitem 2 GB. Czy uruchamiasz to w procesie 32-bitowym? Nie mogłem znaleźć żadnej konkretnej dokumentacji na ten temat, ale tutaj jest pytanie o to samo, co MSDN forum. Możesz spróbować uruchomić to w procesie 64-bitowym.

Zgadzam się jednak, że lepszym rozwiązaniem może być użycie pliku mapowanego w pamięci.

+0

Jestem w 64-bitowym systemie Win7! Jednak nie sądzę, że to problem FileStream.Write: debugowałem go (po dekompilacji mscorlib)! – Atropo

+0

To bardzo możliwe. Rozumiem, że .NET jest nadal ograniczony do 32 bitowych procesów lub limitu 2 GB pamięci. Ale nie przeznaczasz więcej niż 2 GB, więc wątpię, że to jest problem. –

+0

czy cel aplikacji .net "Any CPU" lub x86? –

1

Znalazłem to z MSDN. Czy to może być powiązane? Brzmi dla mnie, że każdy plik ma jeden współdzielony globalnie wskaźnik.

Gdy obiekt FileStream nie ma wyłącznego zawieszenia na uchwycie, inny wątek może jednocześnie uzyskać dostęp do uchwytu pliku i zmienić położenie wskaźnika pliku systemu operacyjnego związanego z uchwytem pliku. W takim przypadku może być zagrożona pozycja zapisana w pamięci podręcznej w obiekcie FileStream i buforowane dane w buforze. Obiekt FileStream rutynowo wykonuje kontrole metod uzyskujących dostęp do buforowanego bufora, aby upewnić się, że położenie uchwytu systemu operacyjnego jest takie samo, jak położenie pamięci podręcznej używane przez obiekt FileStream.

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

+0

Zgodnie z dokumentacją, wydaje się wystarczające użycie "FileOptions.WriteThrough" do wyłączenia każdej pamięci podręcznej między 'FileStream.Write' a dyskiem; ale wciąż obserwuję obecność 'ReadFile' podczas testów. – Atropo