2009-12-10 12 views
10

Pracuję nad aplikacją przeznaczoną dla systemów desktopowych, które mogą mieć tylko 256 MB pamięci RAM (Windows 2000 i nowsze). W mojej aplikacji mam ten duży plik (> 256 MB), który zawiera stałe zapisy około 160 bajtów na każdy. Ta aplikacja ma dość długi proces, w którym z czasem będzie losowo uzyskiwać dostęp do około 90% pliku (do odczytu i zapisu). Dowolny zapis zapisu nie będzie większy niż 1000 rekordów odczytywanych z odczytu danego rekordu (mogę dostroić tę wartość).CreateFileMapping, MapViewOfFile, jak uniknąć trzymania pamięci systemowej

Mam dwie oczywiste opcje tego procesu: regularne operacje we/wy (FileRead, FileWrite) i mapowanie pamięci (CreateFileMapping, MapViewOfFile). Te ostatnie powinny być znacznie wydajniejsze w systemach z wystarczającą pamięcią, ale w systemach z małą pamięcią wymienią większość pamięci innych aplikacji, co w mojej aplikacji jest nie-nie. Czy istnieje sposób na powstrzymanie procesu od zjedzenia całej pamięci (np. Jak zmuszenie do przepłukiwania stron pamięci, do których nie mam dostępu)? Jeśli nie jest to możliwe, muszę odwołać się do zwykłego wejścia/wyjścia; Chciałbym użyć nakładających się we/wy dla części do pisania (ponieważ dostęp jest tak losowy), ale dokumentacja mówi: writes of less than 64K are always served synchronously.

Wszelkie pomysły na ulepszenie operacji wejścia/wyjścia są mile widziane.

+0

Być może VirtualFree (MEM_DECOMMIT) może być pomocny? Nie znam tego. –

+1

Nie, VirtualFree (MEM_DECOMMIT) kończy się niepowodzeniem dla FRP; Właśnie sprawdziłem. –

Odpowiedz

10

W końcu znalazłem sposób, wyprowadzony z wątku here. Sztuczka polega na użyciu VirtualUnlock() na zakresach, które muszę anulować; chociaż ta funkcja zwraca FALSE z błędem 0x9e ("Segment jest już odblokowany"), pamięć jest faktycznie zwolniona, nawet jeśli strony zostały zmodyfikowane (plik jest poprawnie zaktualizowany).

Oto mój przykładowy program testowy:

#include "stdafx.h" 

void getenter(void) 
{ 
    int  ch; 
    for(;;) 
    { 
     ch = getch(); 
     if(ch == '\n' || ch == '\r') return; 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    char* fname = "c:\\temp\\MMFTest\\TestFile.rar";  // 54 MB 
    HANDLE hfile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); 
    if(hfile == INVALID_HANDLE_VALUE) 
    { 
     fprintf(stderr, "CreateFile() error 0x%08x\n", GetLastError()); 
     getenter(); 
     return 1; 
    } 

    HANDLE map_handle = CreateFileMapping(hfile, NULL, PAGE_READWRITE | SEC_RESERVE, 0, 0, 0); 
    if(map_handle == NULL) 
    { 
     fprintf(stderr, "CreateFileMapping() error 0x%08x\n", GetLastError()); 
     getenter(); 
     CloseHandle(hfile); 
     return 1; 
    } 

    char* map_ptr = (char*) MapViewOfFile(map_handle, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); 
    if(map_ptr == NULL) 
    { 
     fprintf(stderr, "MapViewOfFile() error 0x%08x\n", GetLastError()); 
     getenter(); 
     CloseHandle(map_handle); 
     CloseHandle(hfile); 
     return 1; 
    } 

    // Memory usage here is 704KB 
    printf("Mapped.\n"); getenter(); 

    for(int n = 0 ; n < 10000 ; n++) 
    { 
     map_ptr[n*4096]++; 
    } 

    // Memory usage here is ~40MB 
    printf("Used.\n"); getenter(); 

    if(!VirtualUnlock(map_ptr, 5000 * 4096)) 
    { 
     // Memory usage here is ~20MB 
     // 20MB already freed! 
     fprintf(stderr, "VirtualUnlock() error 0x%08x\n", GetLastError()); 
     getenter(); 
     UnmapViewOfFile(map_ptr); 
     CloseHandle(map_handle); 
     CloseHandle(hfile); 
     return 1; 
    } 

    // Code never reached 
    printf("VirtualUnlock() executed.\n"); getenter(); 

    UnmapViewOfFile(map_ptr); 
    CloseHandle(map_handle); 
    CloseHandle(hfile); 

    printf("Unmapped and closed.\n"); getenter(); 

    return 0; 
} 

Jak widać, zestaw roboczy programu jest zmniejszona po wykonaniu VirtualUnlock(), tak jak potrzeba. Muszę tylko śledzić strony, które zmieniam, aby odblokować w razie potrzeby.

+0

Świetnie, dzięki za udostępnienie tego. Rozpaczliwie szukałem sposobu na kontrolowanie ilości pamięci używanej przez pliki mapowane w pamięci i traciłem już nadzieję, że da się to zrobić. Mam tylko nadzieję, że MS nie wyłączy tego systemu. Po tym wszystkim, dlaczego nie możemy po prostu zatwierdzić i anulować strony zgodnie z oczekiwaniami? – Suma

+0

Uwaga: kod błędu, który opisujesz, wydaje się być oczekiwany na podstawie uwagi do dokumentu VirtualUnlock: "Jeśli którakolwiek ze stron w podanym zakresie nie jest zablokowana, VirtualUnlock usuwa takie strony z zestawu roboczego, ustawia ostatni błąd na ERROR_NOT_LOCKED, i zwraca FALSE. " – Suma

+0

Jeszcze jedna uwaga: eksperyment jest nieco mylący. Zestaw roboczy procesu jest zmniejszony, ale to niekoniecznie oznacza, że ​​strona jest odrzucana. Jeśli zmapujesz ponownie, możesz zobaczyć, że otrzymujesz go natychmiast, bez żadnej aktywności na stronie. Zobacz mój eksperyment na http://stackoverflow.com/questions/3525202/how-can-i-decommit-a-file-mapped-page/3525266#3525266 – Suma

1

Czy mapujesz cały plik jako jeden blok za pomocą MapViewOfFile? Jeśli tak, spróbuj mapować mniejsze części. Możesz przepłukać widok za pomocą FlushViewOfFile()

+0

Nie uzyskuję dostępu do pliku sekwencyjnie; istnieje losowy wzorzec, którego nie mogę kontrolować, aby uzyskać dostęp do pliku, więc mapowanie jego małych części będzie bardzo mało efektywne. Poza tym FlushViewOfFile() nie zwalnia żadnej pamięci; wymusza tylko pisanie brudnych stron. –

3

Po prostu zmapuj cały plik do pamięci. Spowoduje to zużycie wirtualnej, ale nie fizycznej pamięci. Plik jest odczytywany z dysku i jest eksmitowany z pamięci za pomocą tych samych zasad, które regulują plik wymiany.

+4

To właśnie jest problem. Zasady, które rządzą plikiem wymiany, mają tendencję do przechowywania zawartości w pamięci znacznie więcej niż w pamięci podręcznej plików, więc uzyskanie dostępu do zmapowanego pliku w końcu zamieni większość innych procesów. –

2

Funkcja VirtualUnlock nie działa. Musisz tylko zadzwonić pod numer FlushViewOfFile (map_ptr, 0) bezpośrednio przed UnmapViewOfFile (map_ptr). Menedżer zadań systemu Windows nie będzie wyświetlać zużycia pamięci fizycznej. Użyj ProcessExplorer z SysInternals

Powiązane problemy