
Tłumaczenie artykułu przygotowane dla studentów kursu .
Wcześniej opisałem, jak sprawdzić i włączyć użycie Hugepages w systemie Linux.
Ten artykuł będzie użyteczny tylko wtedy, gdy naprawdę masz zastosowanie dla Hugepages. Spotkałem wielu ludzi, którzy oszukują się perspektywą, że Hugepages w magiczny sposób zwiększą wydajność. Niemniej jednak hugepaging jest skomplikowanym tematem i przy niewłaściwym użyciu może obniżyć wydajność.
Część 1: sprawdzamy, czy hugepages są włączone w systemie Linux (oryginał )
Problem:
Trzeba sprawdzić, czy HugePages są włączone w twoim systemie.
Rozwiązanie:
To dosyć proste:
cat /sys/kernel/mm/transparent_hugepage/enabledOtrzymasz coś takiego:
always [madvise] neverZobaczysz listę dostępnych opcji (always, madvise, never), z aktualnie aktywną opcją ujętą w nawiasie (domyślnie madvise).
madvise oznacza, że transparent hugepages są włączane tylko dla obszarów pamięci, które wyraźnie żądają hugepages za pomocą .
always oznacza, że transparent hugepages są zawsze włączone dla wszystkich procesów. Zazwyczaj zwiększa to wydajność, ale jeśli masz zastosowanie, w którym wiele procesów pobiera niewielką ilość pamięci, to całkowite obciążenie pamięci może znacznie wzrosnąć.
never oznacza, że transparent hugepages nie będą włączane nawet na żądanie przez madvise. Aby dowiedzieć się więcej, zapoznaj się z jądrem Linux.
Jak zmienić domyślną wartość
Opcja 1: Zmienić bezpośrednio sysfs (po ponownym uruchomieniu parametr wróci do wartości domyślnej):
echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabledOpcja 2: Zmień domyślną wartość systemową, rekompilując jądro z zmienioną konfiguracją (ten wariant zalecany jest tylko, jeśli używasz własnego jądra):
- Aby ustawić always jako domyślną, użyj:
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y # Oznacz CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y jako nieaktywny - Aby ustawić madvise jako domyślną, użyj:
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y # Oznacz CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y jako nieaktywny
Część 2: Zalety i wady HugePages
Spróbujemy wyjaśnić zalety, wady i możliwe błędy związane z używaniem Hugepages. Ponieważ technicznie skomplikowany i pedantyczny artykuł prawdopodobnie będzie trudny do zrozumienia dla osób, które oszukują się uważając, że Hugepages są panaceum, poświęcę dokładność na rzecz prostoty. Po prostu warto pamiętać, że wiele tematów jest naprawdę skomplikowanych i dlatego zostało znacznie uproszczonych.
Zwróć uwagę, że mówimy o 64-bitowych systemach x86 działających na Linuxie, i że zakładam, że system obsługuje transparent hugepages (ponieważ to, że hugepages nie są podmieniane, nie jest wadą), co dzieje się praktycznie w każdej nowoczesnej środowisku Linux.
W poniższych linkach zamieszczę więcej technicznych opisów.
Pamięć wirtualna
Jeśli jesteś programistą C++, wiesz, że obiekty w pamięci mają konkretne adresy (wartości wskaźników).
Jednak te adresy niekoniecznie odzwierciedlają fizyczne adresy w pamięci (adresy w RAM). Reprezentują one adresy w pamięci wirtualnej. Procesor ma specjalny moduł MMU (jednostka zarządzania pamięcią), który pomaga jądru mapować pamięć wirtualną na fizyczne miejsce.
Takie podejście ma wiele zalet, ale najważniejsze z nich to:
- Wydajność (z różnych powodów);
- Izolacja programów, czyli żadna z programów nie może czytać z pamięci innego programu.
Czym są strony?
Pamięć wirtualna jest podzielona na strony. Każda pojedyncza strona wskazuje na określoną pamięć fizyczną, może wskazywać na obszar w pamięci RAM lub na adres przypisany fizycznemu urządzeniu, na przykład karcie graficznej.
Większość stron, z którymi się spotykasz, wskazuje albo na RAM, albo są podmieniane (swap), to znaczy przechowywane na dysku twardym lub SSD. Jądro zarządza fizyczną lokalizacją każdej strony. Jeśli dostęp do podmienionej strony ma miejsce, jądro zatrzymuje wątek, który próbuje uzyskać dostęp do pamięci, odczytuje stronę z dysku twardego/SSD do pamięci RAM, a następnie kontynuuje działanie wątku.
Ten proces jest przezroczysty dla wątku, to znaczy, że nieczyta bezpośrednio z dysku twardego/SSD. Rozmiar normalnych stron wynosi 4096 bajtów. Rozmiar Hugepages wynosi 2 megabajty.
Bufor asocjacyjnej translacji (TLB)
Gdy program odwołuje się do pewnej strony pamięci, procesor musi wiedzieć, z którą fizyczną stroną odczytać dane (to znaczy posiadać wirtualną mapę adresów).
W jądrze znajduje się struktura danych (tabela stron), która zawiera wszystkie informacje o używanych stronach. Za pomocą tej struktury danych można mapować adres wirtualny na adres fizyczny.
Jednak tabela stron jest dość skomplikowana i działa wolno, więc nie możemy za każdym razem analizować całej struktury danych, gdy którykolwiek proces uzyskuje dostęp do pamięci.
Na szczęście w naszym procesorze znajduje się TLB, które buforuje mapowanie adresów wirtualnych i fizycznych. Oznacza to, że mimo że musimy przeanalizować tabelę stron przy pierwszej próbie dostępu, wszystkie kolejne żądania do strony mogą być obsługiwane w TLB, co zapewnia szybką pracę.
Ponieważ jest realizowany jako fizyczne urządzenie (co czyni go przede wszystkim szybkim), jego pojemność jest ograniczona. Dlatego, jeśli zechcesz uzyskać dostęp do większej liczby stron, TLB nie będzie w stanie przechować mapowania dla wszystkich z nich, co spowoduje, że Twój program będzie działał znacznie wolniej.
Hugepages przychodzą z pomocą
Co więc możemy zrobić, aby uniknąć przepełnienia TLB? (Zakładamy, że program nadal potrzebuje tej samej ilości pamięci).
Wtedy pojawiają się Hugepages. Zamiast 4096 bajtów, które wymagają tylko jednego wpisu w TLB, jeden wpis w TLB może teraz wskazywać na ogromne 2 megabajty. Zakładając, że TLB ma 512 wpisów, tutaj bez Hugepages możemy mapować:
4096 b⋅512=2 MBPodczas gdy z nimi możemy mapować:
2 MB⋅512=1 GBDlatego Hugepages są świetne. Mogą zwiększyć wydajność bez dużego wkładu pracy. Ale istnieją tutaj istotne zastrzeżenia.
Zamiana Hugepages
Jądro automatycznie śledzi częstotliwość użycia każdej strony pamięci. Jeśli pamięci fizycznej (RAM) jest niewystarczająco, jądro przeniesie mniej ważne (rzadziej używane) strony na dysk twardy, aby zwolnić część RAM dla ważniejszych stron.
W zasadzie to samo dotyczy Hugepages. Jednak jądro może wymieniać tylko całe strony, a nie pojedyncze bajty.
Załóżmy, że mamy taki program:
char* mymemory = malloc(2*1024*1024); // Przyjmujemy to jako jedną Hugepage!
// Wypełniamy mymemory danymi
// Robimy wiele innych rzeczy,
// które spowodują zamianę strony mymemory
// ...
// Żądamy dostępu tylko do pierwszego bajtu
putchar(mymemory[0]); W takim przypadku jądro będzie musiało odczytać całe 2 megabajty informacji z dysku twardego/SSD tylko po to, abyś mógł odczytać jeden bajt. Jeśli chodzi o zwykłe strony, z dysku twardego/SSD należy odczytać zaledwie 4096 bajtów.
Dlatego, jeśli hugepage jest zastępowany, jego odczyt odbywa się szybciej, tylko jeśli potrzebujesz uzyskać dostęp do całej strony. Oznacza to, że jeśli próbujesz uzyskać dostęp losowo do różnych części pamięci i po prostu odczytujesz kilka kilobajtów, powinieneś używać zwykłych stron i nie martwić się niczym innym.
Z drugiej strony, jeśli musisz uzyskiwać dostęp do dużej części pamięci w sposób sekwencyjny, hugepages zwiększą twoją wydajność. Niemniej jednak musisz to samodzielnie sprawdzić (a nie na przykładzie abstrakcyjnego oprogramowania) i zobaczyć, co zadziała szybciej.
Alokacja w pamięci
Jeśli piszesz w C, wiesz, że możesz zażądać dowolnie małych (lub prawie dowolnie dużych) ilości pamięci z sterty za pomocą malloc(). Załóżmy, że potrzebujesz 30 bajtów pamięci:
char* mymemory = malloc(30);Programiście może się wydawać, że "żądacie" 30 bajtów pamięci z systemu operacyjnego i zwracacie wskaźnik do pewnej pamięci wirtualnej. Ale w rzeczywistości malloc () to po prostu funkcja C, która wywołuje wewnętrznie funkcje w celu żądania lub zwalniania pamięci z systemu operacyjnego.
Jednak żądanie coraz większej ilości pamięci dla każdej alokacji jest nieefektywne; najprawdopodobniej jakiś segment pamięci został już zwolniony (free()), i możemy go ponownie użyć. malloc() wdraża dość skomplikowane algorytmy do ponownego wykorzystania zwolnionej pamięci.
Z drugiej strony, wszystko odbywa się dla ciebie bezszelestnie, dlaczego więc powinno cię to martwić? A dlatego, że wywołanie free() nie oznacza, że .
Istnieje takie pojęcie jak fragmentacja pamięci. W skrajnych przypadkach istnieją segmenty sterty, w których używane są tylko kilka bajtów, podczas gdy wszystko, co znajduje się między nimi, zostało zwolnione. (free()).
Zwróć uwagę, że fragmentacja pamięci to niezwykle złożony temat, a nawet drobne zmiany w programie mogą mieć znaczący wpływ na ten problem. W większości przypadków programy nie powodują znacznej fragmentacji pamięci, ale powinieneś pamiętać, że jeśli problem z fragmentacją pojawia się w pewnym obszarze stosu, hugepages mogą tylko pogorszyć sytuację.
Selektywne zastosowanie hugepages
Po przeczytaniu artykułu, określiłeś, które części twojego programu mogą skorzystać z zastosowania hugepages, a które – nie. Czy w ogóle warto włączyć hugepages?
Na szczęście możesz użyć madvise(), aby włączyć hugepaging tylko dla tych obszarów pamięci, gdzie będzie to korzystne.
Na początek sprawdź, czy hugepages działają w trybie madvise(), za pomocą na początku artykułu.
Następnie użyj madvise(), aby wskazać jądru, gdzie dokładnie zastosować hugepages.
#include <sys/mman.h>
// Аллоцируйте большое количество памяти, которую будете использовать
size_t size = 256*1024*1024;
char* mymemory = malloc(size);
// Просто включите hugepages…
madvise(mymemory, size, MADV_HUGEPAGE);
// … и задайте следующее
madvise(mymemory, size, MADV_HUGEPAGE | MADV_SEQUENTIAL)Zwróć uwagę, że ta metoda to tylko zalecenia dla jądra dotyczące zarządzania pamięcią. To nie oznacza, że jądro automatycznie wykorzysta hugepages dla zadanej pamięci.
Zapoznaj się z dokumentacją , aby dowiedzieć się więcej o zarządzaniu pamięcią oraz madvise(), ten temat ma niezwykle stromy krzywy kształt uczenia się. Dlatego jeśli zamierzasz naprawdę dobrze go zrozumieć, przygotuj się na czytanie i testowanie przez kilka tygodni, zanim oczekujesz jakichkolwiek pozytywnych rezultatów.
Co czytać?
Masz pytanie? Napisz w komentarzach!
Źródło: habr.com
