Jakie są zalety plików mapowanych w pamięci?

89

Szukałem plików mapowanych w pamięci do projektu i byłbym wdzięczny za wszelkie przemyślenia od osób, które albo używały ich wcześniej, albo zdecydowały się ich nie używać, i dlaczego?

W szczególności niepokoją mnie następujące, w kolejności od najważniejszych:

  • konkurencja
  • losowy dostęp
  • występ
  • łatwość użycia
  • ruchliwość
robottobor
źródło

Odpowiedzi:

56

Myślę, że zaletą jest naprawdę to, że redukujesz ilość wymaganych kopii danych w porównaniu z tradycyjnymi metodami odczytu pliku.

Jeśli aplikacja może korzystać z danych „na miejscu” w pliku mapowanym w pamięci, może wejść bez kopiowania; jeśli używasz wywołania systemowego (np. pread () Linuksa), to zazwyczaj oznacza to, że jądro kopiuje dane z własnych buforów do przestrzeni użytkownika. To dodatkowe kopiowanie nie tylko wymaga czasu, ale zmniejsza efektywność pamięci podręcznych procesora poprzez dostęp do tej dodatkowej kopii danych.

Jeśli dane faktycznie muszą być odczytywane z dysku (jak w przypadku fizycznego wejścia / wyjścia), to system operacyjny nadal musi je odczytać, błąd strony prawdopodobnie nie jest lepszy pod względem wydajności niż wywołanie systemowe, ale jeśli one nie (tj. już w pamięci podręcznej systemu operacyjnego), teoretycznie wydajność powinna być znacznie lepsza.

Z drugiej strony nie ma asynchronicznego interfejsu do plików mapowanych w pamięci - jeśli spróbujesz uzyskać dostęp do strony, która nie jest zamapowana, generuje błąd strony, a następnie sprawia, że ​​wątek czeka na I / O.


Oczywistą wadą plików mapowanych w pamięci jest 32-bitowy system operacyjny - łatwo może zabraknąć przestrzeni adresowej.

MarkR
źródło
4
Przynajmniej w systemie Windows możesz mapować wiele 32-bitowych widoków większego pliku mmap - co może być bardziej wydajne niż próbowanie radzenia sobie z bardzo dużymi plikami za pomocą zwykłej funkcji CRT
Martin Beckett
@MarkR Napisałeś "jego dodatkowe kopiowanie nie tylko wymaga czasu, ale zmniejsza efektywność pamięci podręcznych procesora poprzez dostęp do tej dodatkowej kopii danych. ". ( podkreślenie moje). Czy możesz wyjaśnić, w jaki sposób dodatkowa kopia bufora w jądrze utrudnia efektywność pamięci podręcznych procesora?
Geek
4
@ Geek uzyskuje dostęp do dwukrotnie większej ilości pamięci = dwukrotnie więcej zmarnowanej pamięci podręcznej (w przybliżeniu).
user253751
49

Użyłem pliku mapowanego w pamięci, aby zaimplementować funkcję „autouzupełniania”, gdy użytkownik pisze. Mam ponad 1 milion numerów katalogowych produktów przechowywanych w jednym pliku indeksu. Plik zawiera typowe informacje nagłówkowe, ale większość pliku to gigantyczna tablica rekordów o stałym rozmiarze posortowanych według pola klucza.

W czasie wykonywania plik jest mapowany w pamięci, rzutowany na tablicę w Cstylu structa, a my wykonujemy wyszukiwanie binarne, aby znaleźć pasujące numery części zgodnie z typami użytkownika. Tylko kilka stron pamięci pliku jest faktycznie odczytywanych z dysku - niezależnie od tego, które strony zostaną trafione podczas wyszukiwania binarnego.

  • Współbieżność - miałem problem z implementacją, w wyniku którego czasami wielokrotnie mapował plik w tej samej przestrzeni procesu. O ile pamiętam, był to problem, ponieważ czasami system nie mógł znaleźć wystarczająco dużego wolnego bloku pamięci wirtualnej, aby zmapować plik. Rozwiązaniem było mapowanie pliku tylko raz i przekazywanie do niego wszystkich wywołań. Z perspektywy czasu korzystanie z pełnej usługi Windows byłoby fajne.
  • Losowy dostęp - wyszukiwanie binarne jest z pewnością losowym dostępem i błyskawicznie
  • Wydajność - wyszukiwanie jest niezwykle szybkie. Gdy użytkownicy wpisują, w wyskakującym okienku jest wyświetlana lista pasujących numerów części produktów, lista kurczy się w miarę wpisywania. Podczas pisania nie ma zauważalnego opóźnienia.
Brian Ensink
źródło
1
Czy wyszukiwanie binarne nie byłoby powolne, ponieważ strony są wczytywane przy każdej próbie? A może system operacyjny jest wystarczająco inteligentny, aby poradzić sobie z tym w efektywny sposób?
jjxtra
1
Przypuszczam, że używanie operacji we / wy mapowanych w pamięci jest trochę marnotrawstwem dla wyszukiwania binarnego, ponieważ wyszukiwanie będzie miało dostęp tylko do kilku pojedynczych kluczy w stosunkowo odległych lokalizacjach pamięci, ale system operacyjny załaduje strony 4k dla każdego takiego żądania. Ale z drugiej strony plik z częściami nie zmienia się zbytnio, więc pamięć podręczna pomaga to ukryć. Ale mówiąc ściśle, uważam, że tradycyjne poszukiwanie / czytanie byłoby tutaj lepsze. Wreszcie 1 milion to niewiele w dzisiejszych czasach. Dlaczego nie przechowywać tego wszystkiego w pamięci RAM?
świnia
5
@the swine and PsychoDad, moja oryginalna odpowiedź pochodziła z 2008 roku, a rzeczywista implementacja tej funkcji automatycznego uzupełniania mapowana na pamięć miała miejsce około 2004-2005 roku. Zużywanie 800-1000 MB pamięci fizycznej do załadowania całego pliku nie było dobrym rozwiązaniem dla naszej bazy użytkowników. Rozwiązanie mapowane w pamięci było bardzo szybkie i wydajne. To kopało i wspominam to czule z moich wczesnych dni jako młodszych programistów. :)
Brian Ensink
@BrianEnsink: ok, to ma sens. Nie spodziewałem się, że każdy wpis będzie miał aż 1kB. wtedy oczywiście podejście stronicowane staje się bardziej wydajne. fajnie :)
świnia
22

Pliki mapowane w pamięci mogą być używane do zastępowania dostępu do odczytu / zapisu lub do obsługi współbieżnego udostępniania. Kiedy używasz ich do jednego mechanizmu, otrzymujesz także drugi.

Zamiast szukać, pisać i czytać w pliku, mapujesz go do pamięci i po prostu uzyskujesz dostęp do bitów tam, gdzie się spodziewasz.

Może to być bardzo przydatne iw zależności od interfejsu pamięci wirtualnej może poprawić wydajność. Poprawa wydajności może nastąpić, ponieważ system operacyjny może teraz zarządzać tym byłym „operacją we / wy” wraz z całym innym dostępem do pamięci programowej i może (teoretycznie) wykorzystać algorytmy stronicowania i tak dalej, które już wykorzystuje do obsługi pamięć wirtualna dla pozostałej części programu. Zależy to jednak od jakości podstawowego systemu pamięci wirtualnej. Słyszałem anegdoty, które mówią, że systemy pamięci wirtualnej Solaris i * BSD mogą wykazywać lepszą wydajność niż system VM w systemie Linux - ale nie mam danych empirycznych, które mogłyby to potwierdzić. YMMV.

Współbieżność pojawia się, gdy weźmie się pod uwagę możliwość korzystania przez wiele procesów z tego samego „pliku” w pamięci mapowanej. W modelu odczytu / zapisu, jeśli dwa procesy zapisałyby w tym samym obszarze pliku, można było być prawie pewnym, że jeden z danych procesu dotrze do pliku, nadpisując dane drugiego procesu. Dostaniesz jedną lub drugą - ale nie jakieś dziwne mieszanie się. Muszę przyznać, że nie jestem pewien, czy jest to zachowanie nakazane przez jakikolwiek standard, ale jest to coś, na czym można w zasadzie polegać. (Właściwie to dobre pytanie uzupełniające!)

Dla kontrastu, wyobraź sobie dwa procesy „pisania” na mapie. Robią to, robiąc „magazyny pamięci”, co skutkuje stronicowaniem danych przez system operacyjny na dysk - ostatecznie. Ale w międzyczasie można się spodziewać nakładania się zapisów.

Oto przykład. Powiedzmy, że mam dwa procesy, oba zapisują 8 bajtów z przesunięciem 1024. Proces 1 zapisuje „11111111”, a proces 2 zapisuje „22222222”. Jeśli używają wejścia / wyjścia pliku, możesz sobie wyobrazić, że głęboko w systemie operacyjnym znajduje się bufor pełen 1s i bufor pełny 2s, oba kierowane w to samo miejsce na dysku. Jeden z nich dotrze tam pierwszy, a drugi sekundę. W tym przypadku wygrywa ten drugi. Jeśli jednak używam podejścia opartego na plikach mapowanych w pamięci, proces 1 przejdzie do magazynu pamięci o wielkości 4 bajtów, a następnie do innego magazynu pamięci o wielkości 4 bajtów (załóżmy, że nie jest to maksymalny rozmiar magazynu pamięci). Proces 2 będzie robił to samo. W zależności od tego, kiedy procesy są uruchomione, możesz spodziewać się następujących informacji:

11111111
22222222
11112222
22221111

Rozwiązaniem tego problemu jest zastosowanie jawnego wzajemnego wykluczenia - co jest prawdopodobnie dobrym pomysłem w każdym przypadku. W każdym razie polegałeś na systemie operacyjnym, który zrobi „właściwą rzecz” w przypadku odczytu / zapisu pliku we / wy.

Prymitywem klasyfikacji wzajemnego wykluczania jest muteks. W przypadku plików mapowanych w pamięci sugerowałbym przyjrzenie się muteksowi mapowanemu w pamięci, dostępnemu za pomocą (np.) Pthread_mutex_init ().

Edytuj za pomocą jednej gotcha: Kiedy używasz zmapowanych plików, istnieje pokusa, aby osadzić wskaźniki do danych w pliku, w samym pliku (myślę, że połączona lista przechowywana w zmapowanym pliku). Nie chcesz tego robić, ponieważ plik może być mapowany pod różnymi adresami bezwzględnymi w różnym czasie lub w różnych procesach. Zamiast tego użyj przesunięć w mapowanym pliku.

bagno
źródło
1

Problemem byłaby współbieżność. Losowy dostęp jest łatwiejszy Wydajność jest dobra lub świetna. Łatwość użycia. Nie tak dobre. Przenośność - nie tak gorąco.

Użyłem ich w systemie Sun dawno temu i to są moje myśli.

Paul Nathan
źródło