Pracuję nad programem, który będzie przetwarzał pliki o rozmiarze potencjalnie 100 GB lub większym. Pliki zawierają zestawy rekordów o zmiennej długości. Mam pierwszą implementację uruchomioną i teraz dążę do poprawy wydajności, szczególnie w zakresie wydajniejszego wykonywania operacji we / wy, ponieważ plik wejściowy jest skanowany wiele razy.
Czy istnieje ogólna zasada używania mmap()
versus czytania w blokach za pośrednictwem biblioteki C ++ fstream
? Chciałbym wczytać duże bloki z dysku do bufora, przetworzyć pełne rekordy z bufora, a następnie przeczytać więcej.
mmap()
Kod może potencjalnie uzyskać bardzo brudny od mmap
„d bloki muszą leżeć na stronę rozmiarze granice (moim rozumieniu) i zapisuje potencjalnie jak przez granice strona. Używając fstream
s, mogę po prostu szukać początku rekordu i zacząć czytać ponownie, ponieważ nie ograniczamy się do czytania bloków, które leżą na granicach wielkości strony.
Jak mogę wybrać między tymi dwiema opcjami bez wcześniejszego spisania pełnej implementacji? Wszelkie zasady praktyczne (np. mmap()
2x szybsze) lub proste testy?
mmap()
jest 2-6 razy szybsza niż przy użyciu syscalls, npread()
.Odpowiedzi:
Próbowałem znaleźć ostatnie słowo na temat wydajności mmap / read w Linuksie i natrafiłem na fajny post ( link ) na liście mailingowej jądra Linuksa. Jest od 2000 roku, więc od tego czasu wprowadzono wiele ulepszeń we / wy i pamięci wirtualnej w jądrze, ale ładnie wyjaśnia powód, dla którego
mmap
lubread
może być szybszy lub wolniejszy.mmap
ma większy narzut niżread
(podobnie jakepoll
ma większy narzut niżpoll
, który ma większy narzut niżread
). Zmiana odwzorowań pamięci wirtualnej jest dość kosztowną operacją na niektórych procesorach z tych samych powodów, dla których przełączanie między różnymi procesami jest kosztowne.Jednak,
read
plik mógł zostać usunięty z pamięci podręcznej przed wiekami. Nie dotyczy to korzystania z pliku i natychmiastowego jego odrzucenia. (Jeśli próbujeszmlock
przechodzić między stronami tylko po to, by przechowywać je w pamięci podręcznej, próbujesz przechytrzyć pamięć podręczną dysku, a tego rodzaju oszustwo rzadko poprawia wydajność systemu).Dyskusja mmap / read przypomina mi dwie inne dyskusje dotyczące wydajności:
Niektórzy programiści Java byli zszokowani odkryciem, że nieblokujące operacje we / wy są często wolniejsze niż blokowanie operacji we / wy, co ma idealny sens, jeśli wiadomo, że nieblokujące operacje we / wy wymagają większej liczby wywołań systemowych.
Niektórzy inni programiści sieci byli zszokowani, gdy dowiedzieli się, że
epoll
często jest wolniejszy niżpoll
, co ma sens, jeśli wiesz, że zarządzanieepoll
wymaga wykonania większej liczby połączeń systemowych.Wniosek: używaj map pamięci, jeśli masz dostęp do danych losowo, trzymaj je przez długi czas lub jeśli wiesz, że możesz je udostępnić innym procesom (
MAP_SHARED
nie jest to bardzo interesujące, jeśli nie ma rzeczywistego udostępniania). Odczytuj pliki normalnie, jeśli uzyskujesz dostęp do danych sekwencyjnie lub odrzucasz je po odczytaniu. A jeśli którakolwiek z metod sprawia, że Twój program jest mniej złożony, zrób to . W wielu rzeczywistych przypadkach nie ma pewnego sposobu, aby pokazać, że jest on szybszy bez przetestowania rzeczywistej aplikacji, a NIE testu.(Przepraszam, że potrzebuję tego pytania, ale szukałem odpowiedzi, a to pytanie wciąż pojawiało się u góry wyników Google).
źródło
mmap
vsread()
w tym wątku jest nadal prawdą, jak to miało miejsce w przeszłości, ogólnej wydajności nie można tak naprawdę ustalić, dodając zalety i wady, a jedynie testując na konkretnej konfiguracji sprzętowej. Na przykład można dyskutować, że „Wywołanie mmap ma narzut większy niż odczyt” - takmmap
musi dodać mapowania do tabeli stron procesu, aleread
musi skopiować wszystkie odczytane bajty z jądra do przestrzeni użytkownika.mmap
koszty są niższe niż wread
przypadku odczytów większych niż rozmiar strony (4 KiB). Teraz jest prawdą, że jeśli chcesz mieć dostęp do danych rzadko i losowo,mmap
to naprawdę, naprawdę dobrze - ale odwrotność nie jest konieczna prawda:mmap
może nadal być najlepsza dla dostępu sekwencyjnego.mmap
jest szybszy, spodziewam się zobaczyć co najmniej cały aparat testowy (kod źródłowy) z tabelarycznymi wynikami oraz numer modelu procesora.mmap
nie opróżnia TLB, z wyjątkiem nietypowych okoliczności (alemunmap
może). Moje testy obejmowały zarówno znaki mikrodruku (w tymmunmap
), jak i „w aplikacji” działające w rzeczywistym przypadku użycia. Oczywiście moja aplikacja nie jest taka sama jak Twoja, więc ludzie powinni przetestować lokalnie. Nie jest nawet jasne,mmap
czy faworyzuje go mikro-test porównawczy:read()
również uzyskuje duży wzrost, ponieważ bufor docelowy po stronie użytkownika zwykle pozostaje w L1, co może się nie zdarzyć w większej aplikacji. Więc tak, „to skomplikowane”.Głównym kosztem wydajności będzie I / O dysku. „mmap ()” jest z pewnością szybsze niż istream, ale różnica może nie być zauważalna, ponieważ dyskowe operacje we / wy zdominują czasy działania.
Próbowałem fragmentu kodu Bena Collinsa (patrz wyżej / poniżej), aby przetestować jego twierdzenie, że „mmap () jest znacznie szybszy”) i nie znalazłem mierzalnej różnicy. Zobacz moje komentarze do jego odpowiedzi.
Z pewnością nie zalecałbym osobno mapowania każdego rekordu po kolei, chyba że twoje „rekordy” są ogromne - byłoby to strasznie powolne, wymagające 2 wywołań systemowych dla każdego rekordu i prawdopodobnie utraty strony z pamięci podręcznej pamięci dyskowej .... .
W twoim przypadku myślę, że mmap (), istream i niskopoziomowe wywołania open () / read () będą mniej więcej takie same. W takich przypadkach polecam mmap ():
(btw - Kocham mmap () / MapViewOfFile ()).
źródło
mmap jest znacznie szybszy. Możesz napisać prosty test porównawczy, aby to sobie udowodnić:
przeciw:
Oczywiście pomijam szczegóły (np. Jak ustalić, kiedy dojdziesz do końca pliku
page_size
, na przykład , jeśli plik nie jest wielokrotnością ), ale tak naprawdę nie powinno być o wiele bardziej skomplikowane niż to .Jeśli możesz, możesz spróbować rozbić dane na wiele plików, które mogą być mmap () - edytowane w całości zamiast w części (znacznie prościej).
Kilka miesięcy temu miałem na wpół upakowaną implementację klasy stream-window mmap () ed dla boost_iostreams, ale nikogo to nie obchodziło i zajęty byłem innymi rzeczami. Niestety kilka tygodni temu usunąłem archiwum starych niedokończonych projektów i była to jedna z ofiar :-(
Aktualizacja : Powinienem również dodać zastrzeżenie, że ten test porównawczy będzie wyglądał zupełnie inaczej w systemie Windows, ponieważ Microsoft zaimplementował sprytną pamięć podręczną plików, która robi większość tego, co zrobiłbyś z mmap. To znaczy, dla często używanych plików, możesz po prostu zrobić std :: ifstream.read () i byłoby to tak szybkie jak mmap, ponieważ pamięć podręczna plików wykonałaby już dla ciebie mapowanie pamięci i jest przezroczysta.
Ostatnia aktualizacja : Słuchajcie, ludzie: na wielu różnych kombinacjach platform systemu operacyjnego i bibliotek standardowych oraz dysków i hierarchii pamięci nie mogę powiedzieć z całą pewnością, że wywołanie systemowe
mmap
, postrzegane jako czarna skrzynka, zawsze zawsze będzie znacznie szybsze niżread
. To nie było dokładnie moim zamiarem, nawet jeśli moje słowa można by tak interpretować. Ostatecznie chodziło mi o to, że operacje we / wy mapowane w pamięci są generalnie szybsze niż operacje we / wy oparte na bajtach; to wciąż prawda . Jeśli eksperymentalnie okaże się, że nie ma między nimi żadnej różnicy, jedynym uzasadnieniem, które wydaje mi się rozsądne, jest to, że twoja platforma implementuje mapowanie pamięci pod osłonami w sposób korzystny dla wydajności wywołańread
. Jedynym sposobem, aby być absolutnie pewnym, że używasz mapowanego na pamięć we / wy w przenośny sposób, jest użyciemmap
. Jeśli nie zależy Ci na przenośności i możesz polegać na szczególnych cechach docelowych platform, użycieread
może być odpowiednie bez poświęcania wymiernej wydajności.Edytuj, aby wyczyścić listę odpowiedzi: @jbl:
Jasne - pisałem bibliotekę C ++ dla Git (libgit ++, jeśli wolisz) i napotkałem podobny problem: Musiałem być w stanie otwierać duże (bardzo duże) pliki i nie mieć wydajności, aby być totalnym psem (jak by to było z
std::fstream
).Boost::Iostreams
ma już źródło mapowane_pliku, ale problem polegał na tym, że pingowałmmap
całe pliki, co ogranicza cię do 2 ^ (wordsize). Na komputerach 32-bitowych 4 GB nie jest wystarczająco duże. Nie jest nierozsądne oczekiwać, że.pack
pliki w Git będą znacznie większe niż te, więc musiałem czytać plik w częściach bez uciekania się do zwykłego we / wy pliku. Pod osłonąBoost::Iostreams
wdrożyłem Źródło, które jest mniej więcej kolejnym spojrzeniem na interakcje międzystd::streambuf
istd::istream
. Możesz także wypróbować podobne podejście, po prostu dziedziczącstd::filebuf
domapped_filebuf
i podobnie, dziedziczącstd::fstream
doa mapped_fstream
. Trudno jest dobrze zrozumieć interakcję między nimi dwoma.Boost::Iostreams
wykonał dla ciebie część pracy, a także zapewnia zaczepy do filtrów i łańcuchów, więc pomyślałem, że bardziej użyteczne byłoby zaimplementowanie go w ten sposób.źródło
mmap()
kartoteką strony na raz? Jeśli asize_t
jest wystarczająco pojemny, aby pomieścić rozmiar pliku (bardzo prawdopodobne w systemach 64-bitowych), to tylkommap()
cały plik w jednym wywołaniu.Istnieje już wiele dobrych odpowiedzi, które obejmują wiele istotnych punktów, więc dodam tylko kilka problemów, których nie widziałem bezpośrednio powyżej. Oznacza to, że ta odpowiedź nie powinna być uważana za kompleksowy za i przeciw, ale raczej jako dodatek do innych odpowiedzi tutaj.
mmap wydaje się magią
Przyjmując przypadek, w którym plik jest już w pełni buforowany 1 jako linia bazowa 2 ,
mmap
może wydawać się magią :mmap
wymaga tylko 1 wywołania systemowego (potencjalnie) odwzorowania całego pliku, po czym nie są już potrzebne żadne wywołania systemowe.mmap
nie wymaga kopiowania danych pliku z jądra do przestrzeni użytkownika.mmap
umożliwia dostęp do pliku „jako pamięć”, w tym przetwarzanie go za pomocą wszelkich zaawansowanych sztuczek, które można wykonać w stosunku do pamięci, takich jak auto-wektoryzacja kompilatora, elementy wewnętrzne SIMD , pobieranie wstępne, zoptymalizowane procedury analizy w pamięci, OpenMP itp.W przypadku, gdy plik znajduje się już w pamięci podręcznej, wydaje się niemożliwe do pobicia: wystarczy uzyskać bezpośredni dostęp do pamięci podręcznej strony jądra jako pamięci i nie może ona być szybsza.
Cóż, może.
mmap nie jest tak naprawdę magią, ponieważ ...
mmap nadal działa na stronie
Podstawowym ukrytym kosztem
mmap
vsread(2)
(który jest naprawdę porównywalnym syscall na poziomie systemu operacyjnego do odczytu bloków ) jest tommap
, że musisz wykonać „trochę pracy” dla każdej strony 4K w przestrzeni użytkownika, nawet jeśli może być ukryta przez mechanizm błędu strony.Na przykład typowa implementacja, która jest po prostu
mmap
całym plikiem, musi zostać uszkodzona, więc 100 GB / 4K = 25 milionów błędów, aby odczytać plik 100 GB. Będą to drobne błędy , ale 25 miliardów błędów stron wciąż nie będzie super szybkich. Koszt drobnej usterki w najlepszym przypadku to prawdopodobnie setki nanosów.mmap w dużej mierze opiera się na wydajności TLB
Teraz możesz przejść
MAP_POPULATE
do,mmap
aby powiedzieć mu, aby skonfigurował wszystkie tabele stron przed powrotem, więc nie powinno być żadnych błędów strony podczas uzyskiwania dostępu do niej. Problem polega na tym, że wczytuje on cały plik do pamięci RAM, który wybuchnie, jeśli spróbujesz zmapować plik 100 GB - ale zignorujmy go na razie 3 . Jądro musi wykonać pracę na stronie, aby skonfigurować te tabele stron (pokazuje się jako czas jądra). To powoduje, że jest to duży koszt wmmap
podejściu i jest proporcjonalny do rozmiaru pliku (tzn. Nie staje się stosunkowo mniej ważny w miarę wzrostu rozmiaru pliku) 4 .Wreszcie, nawet w przypadku dostępu do przestrzeni użytkownika takie mapowanie nie jest całkowicie darmowe (w porównaniu z dużymi buforami pamięci niepochodzącymi z pliku
mmap
) - nawet po skonfigurowaniu tabel stron, każdy dostęp do nowej strony będzie, koncepcyjnie, popełnisz błąd TLB. Ponieważmmap
ing plik oznacza użycie pamięci podręcznej strony i jego stron 4K, ponosisz ten koszt 25 milionów razy za plik 100 GB.Teraz rzeczywisty koszt tych braków TLB zależy w dużej mierze od co najmniej następujących aspektów twojego sprzętu: (a) ile masz TLB 4K i jak działa reszta buforowania tłumaczenia (b) jak dobrze radzi sobie wstępne pobieranie sprzętu z TLB - np. czy pobieranie wstępne może uruchomić spacer strony? (c) jak szybki i równoległy jest sprzęt do chodzenia po stronach. W nowoczesnych procesorach Intel x86 o wysokiej wydajności sprzęt do chodzenia po stronach jest ogólnie bardzo silny: istnieją co najmniej 2 równoległe chodzenia po stronach, chodzenie po stronie może odbywać się jednocześnie z dalszym wykonywaniem, a wstępne pobieranie sprzętu może uruchamiać chodzenie po stronie. Tak więc wpływ TLB na ładowanie odczytu strumieniowego jest dość niski - i takie obciążenie często działa podobnie bez względu na rozmiar strony. Jednak inny sprzęt jest zwykle znacznie gorszy!
read () pozwala uniknąć tych pułapek
read()
Syscall, czyli to, co zazwyczaj leży u podstaw „blok czytać” Połączenia typu oferowanych na przykład w C, C ++ i innych języków ma jedną podstawową wadę, że każdy jest dobrze świadomy:read()
wywołanie N bajtów musi skopiować N bajtów z jądra do przestrzeni użytkownika.Z drugiej strony pozwala to uniknąć większości powyższych kosztów - nie trzeba mapować 25 milionów stron 4K w przestrzeń użytkownika. Zwykle możesz
malloc
buforować mały bufor w przestrzeni użytkownika i używać go wielokrotnie do wszystkich swoichread
połączeń. Po stronie jądra prawie nie ma problemu ze stronami 4K lub brakami TLB, ponieważ cała pamięć RAM jest zwykle mapowana liniowo przy użyciu kilku bardzo dużych stron (np. 1 GB stron na x86), więc strony leżące w pamięci podręcznej stron są pokryte bardzo wydajnie w przestrzeni jądra.Zasadniczo masz następujące porównanie, aby ustalić, która jest szybsza dla jednego odczytu dużego pliku:
Czy dodatkowe działanie na stronę implikowane przez to
mmap
podejście jest bardziej kosztowne niż praca na bajt kopiowania zawartości pliku z jądra do przestrzeni użytkownika sugerowana przy użyciuread()
?W wielu systemach są one w przybliżeniu zrównoważone. Zauważ, że każdy skaluje się z zupełnie innymi atrybutami sprzętu i stosu systemu operacyjnego.
W szczególności
mmap
podejście staje się stosunkowo szybsze, gdy:MAP_POPULATE
implementację, która może efektywnie przetwarzać duże mapy w przypadkach, gdy na przykład strony leżące obok siebie są sąsiadujące w pamięci fizycznej.... podczas gdy
read()
podejście staje się stosunkowo szybsze, gdy:read()
Syscall ma dobrą wydajność kopiowania. Np. Dobracopy_to_user
wydajność po stronie jądra.Powyższe czynniki sprzętowe są bardzo zróżnicowane na różnych platformach, nawet w tej samej rodzinie (np. W generacjach x86, a zwłaszcza w segmentach rynku) i zdecydowanie w różnych architekturach (np. ARM vs x86 vs PPC).
Czynniki OS również się zmieniają, z różnymi ulepszeniami po obu stronach, powodując duży skok względnej prędkości dla jednego lub drugiego podejścia. Najnowsza lista obejmuje:
mmap
przypadku bezMAP_POPULATE
.copy_to_user
metodarch/x86/lib/copy_user_64.S
, na przykład z wykorzystaniemREP MOVQ
kiedy jest szybka, które naprawdę pomagająread()
sprawę.Aktualizacja po Spectre and Meltdown
Ograniczenie luk w zabezpieczeniach Spectre i Meltdown znacznie zwiększyło koszt wywołania systemowego. W systemach, które zmierzyłem, koszt wywołania systemowego „nic nie rób” (który jest szacunkiem czystego obciążenia wywołania wywołania systemowego, oprócz rzeczywistej pracy wykonanej przez wywołanie), spadł z około 100 ns na typowy nowoczesny system Linux do około 700 ns. Ponadto, w zależności od systemu, poprawka izolacji tabeli stron specjalnie dla Meltdown może mieć dodatkowe skutki w dół, poza bezpośrednim kosztem wywołania systemowego, z powodu konieczności ponownego ładowania wpisów TLB.
Wszystko to jest względną wadą
read()
metod opartych na metodach w porównaniu zmmap
metodami opartymi na metodach, ponieważread()
metody muszą wywoływać jedno wywołanie systemowe dla każdej wartości danych o „rozmiarze bufora”. Nie można arbitralnie zwiększyć rozmiaru bufora, aby amortyzować ten koszt, ponieważ użycie dużych buforów zwykle działa gorzej, ponieważ przekracza się rozmiar L1, a zatem ciągle cierpi na brak pamięci podręcznej.Z drugiej strony za pomocą
mmap
można zmapować w dużym obszarze pamięciMAP_POPULATE
i uzyskać do niego skuteczny dostęp, kosztem tylko jednego wywołania systemowego.1 To mniej więcej obejmuje przypadek, w którym plik nie był w pełni buforowany na początku, ale gdzie system operacyjny do odczytu jest wystarczająco dobry, aby tak się pojawił (tzn. Strona jest zwykle buforowana do czasu chcieć tego). Jest to jednak subtelna kwestia, ponieważ sposób działania z wyprzedzeniem często różni się między połączeniami
mmap
iread
wywołaniami i może być dalej dostosowywany za pomocą połączeń „doradzania”, jak opisano w 2 .2 ... ponieważ jeśli plik nie jest buforowany, twoje zachowanie będzie całkowicie zdominowane przez obawy związane z IO, w tym sympatię twojego wzorca dostępu do podstawowego sprzętu - i dołożymy wszelkich starań, aby taki dostęp był tak sympatyczny jak możliwe, np. poprzez użycie
madvise
lubfadvise
połączenia (i wszelkie zmiany poziomu aplikacji, które możesz wprowadzić, aby poprawić wzorce dostępu).3 Można to obejść, na przykład, sekwencyjnie
mmap
wchodząc w okna o mniejszym rozmiarze, powiedzmy 100 MB.4 W rzeczywistości okazuje się, że
MAP_POPULATE
podejście (przynajmniej jedna kombinacja sprzęt / system operacyjny) jest tylko nieco szybsze niż nieużywanie go, prawdopodobnie dlatego, że jądro używa błędów - więc rzeczywista liczba drobnych błędów jest zmniejszona 16- krotnie lub tak.źródło
mmap
będziesz miał nie do pokonania przewagę, ponieważ unikasz narzutu wywołania stałego jądra. Z drugiej strony,mmap
zwiększa również ciśnienie TLB i faktycznie powoduje spowolnienie w fazie „rozgrzewania”, w której bajty są odczytywane po raz pierwszy w bieżącym procesie (chociaż nadal znajdują się na stronie strony), ponieważ może to zrobić więcej pracy niżread
, na przykład, „obejście” sąsiednich stron ... a dla tych samych aplikacji najważniejsze jest „rozgrzanie”! @CaetanoSauerPrzykro mi, że Ben Collins stracił swój kod źródłowy mmap systemu Windows. Byłoby miło mieć w Boost.
Tak, mapowanie pliku jest znacznie szybsze. Zasadniczo używasz podsystemu pamięci wirtualnej systemu operacyjnego do kojarzenia pamięci z dyskiem i odwrotnie. Pomyśl o tym w ten sposób: jeśli programiści jądra systemu operacyjnego mogliby zrobić to szybciej, zrobiliby to. Ponieważ to sprawia, że prawie wszystko jest szybsze: bazy danych, czasy uruchamiania, czasy ładowania programów i tak dalej.
Podejście do przesuwanego okna nie jest wcale takie trudne, ponieważ można jednocześnie zamapować wiele stron z wieloma stronami. Tak więc rozmiar rekordu nie ma znaczenia, o ile największy z każdego pojedynczego rekordu zmieści się w pamięci. Ważne jest zarządzanie księgowością.
Jeśli rekord nie zaczyna się na granicy getpagesize (), mapowanie musi rozpocząć się na poprzedniej stronie. Długość mapowanego regionu rozciąga się od pierwszego bajtu rekordu (w razie potrzeby zaokrąglanego w dół do najbliższej wielokrotności getpagesize ()) do ostatniego bajtu rekordu (zaokrąglanego w górę do najbliższej wielokrotności getpagesize ()). Po zakończeniu przetwarzania rekordu możesz usunąć mapowanie () i przejść do następnego.
To wszystko działa dobrze również w systemie Windows za pomocą CreateFileMapping () i MapViewOfFile () (i GetSystemInfo (), aby uzyskać SYSTEM_INFO.dwAllocationGranularity --- nie SYSTEM_INFO.dwPageSize).
źródło
mmap powinien być szybszy, ale nie wiem ile. To bardzo zależy od twojego kodu. Jeśli używasz mmapa, najlepiej zmapuj cały plik na raz, co znacznie ułatwi Ci życie. Jednym z potencjalnych problemów jest to, że jeśli Twój plik jest większy niż 4 GB (lub w praktyce limit jest niższy, często 2 GB), będziesz potrzebować architektury 64-bitowej. Więc jeśli używasz środowiska 32, prawdopodobnie nie chcesz go używać.
To powiedziawszy, może istnieć lepsza droga do poprawy wydajności. Powiedziałeś, że plik wejściowy jest skanowany wiele razy , jeśli możesz go odczytać w jednym przebiegu, a następnie zrobić z nim, może to być potencjalnie znacznie szybsze.
źródło
Być może powinieneś wstępnie przetworzyć pliki, aby każdy rekord znajdował się w osobnym pliku (a przynajmniej że każdy plik ma rozmiar mmap).
Czy mógłbyś również wykonać wszystkie kroki przetwarzania dla każdego rekordu, zanim przejdziesz do następnego? Może to pozwoliłoby uniknąć niektórych narzutów we / wy?
źródło
Zgadzam się, że plik mmap'd I / O będzie szybciej, ale w czasie, gdy benchmarking kod, nie powinny być przykładem licznik nieco zoptymalizowane?
Ben Collins napisał:
Proponuję również spróbować:
Poza tym możesz również spróbować ustawić rozmiar bufora na taki sam rozmiar jak jedna strona pamięci wirtualnej, na wypadek gdyby 0x1000 nie był wielkości jednej strony pamięci wirtualnej na twoim komputerze ... IMHO wciąż zapisywało pliki we / wy wygrywa, ale to powinno przybliżyć sprawę.
źródło
Moim zdaniem użycie mmap () „po prostu” odciąża programistę od konieczności pisania własnego kodu pamięci podręcznej. W prostym przypadku „odczytaj raz eactly” nie będzie to trudne (chociaż, jak wskazuje mlbrock, nadal zapisujesz kopię pamięci w przestrzeni procesu), ale jeśli przewijasz plik do przodu lub do tyłu lub pomijanie bitów i tak dalej, uważam, że programiści jądra prawdopodobnie wykonali lepszą robotę implementując buforowanie niż mogę ...
źródło
mmap
buforowania jest to, że po prostu ponownie używasz istniejącej pamięci podręcznej strony, która już tam będzie, dzięki czemu otrzymujesz tę pamięć za darmo i można ją współdzielić między procesami.Pamiętam, jak wiele lat temu mapowałem ogromny plik zawierający strukturę drzewa do pamięci. Byłem zdumiony szybkością w porównaniu z normalną serializacją, która wymaga dużo pracy w pamięci, jak przydzielanie węzłów drzew i ustawianie wskaźników. W rzeczywistości porównywałem pojedyncze wywołanie mmap (lub jego odpowiednika w systemie Windows) z wieloma (WIELE) wywołaniami nowych i wywoływanych przez operatora. W przypadku tego rodzaju zadań mmap jest bezkonkurencyjny w porównaniu do usuwania serializacji. Oczywiście należy w tym celu przyjrzeć się wskaźnikom relokowalnym.
źródło
To brzmi jak dobry przypadek użycia dla wielowątkowości ... Wydaje mi się, że możesz łatwo ustawić jeden wątek do odczytu danych, podczas gdy inny przetwarzają go. Może to być sposób na radykalne zwiększenie postrzeganej wydajności. Tylko myśl.
źródło
Myślę, że największą zaletą mmap jest możliwość asynchronicznego odczytu z:
Problem polega na tym, że nie mogę znaleźć odpowiedniego MAP_FLAGS, który dałby wskazówkę, że ta pamięć powinna być zsynchronizowana z pliku jak najszybciej. Mam nadzieję, że MAP_POPULATE daje właściwą wskazówkę dla mmap (tzn. Nie będzie próbował załadować całej zawartości przed powrotem z wywołania, ale zrobi to asynchronicznie z feed_data). Przynajmniej daje lepsze wyniki z tą flagą, nawet jeśli instrukcja mówi, że nic nie robi bez MAP_PRIVATE od wersji 2.6.23.
źródło
posix_madvise
zWILLNEED
flagą dla leniwych podpowiedzi do wstępnie wypełnić.posix_madvise
jest to wywołanie asynchroniczne. Przydałoby się również odniesieniemlock
do tych, którzy chcą czekać, aż cały region pamięci stanie się dostępny bez błędów strony.