Zalety i wady HugePages

Zalety i wady HugePages

Tłumaczenie artykułu przygotowanego dla studentów kursu „Administrator Linuksa”.

Wcześniej mówiłem o tym, jak testować i włączać Hugepages w systemie Linux.
Ten artykuł będzie przydatny tylko wtedy, gdy rzeczywiście masz miejsce do korzystania z Hugepages. Spotkałem wielu ludzi, których zwiodła perspektywa, że ​​Hugepages w magiczny sposób poprawią produktywność. Jednak ogromne stronicowanie jest złożonym tematem i może obniżyć wydajność, jeśli zostanie użyte nieprawidłowo.

Część 1: Sprawdzanie, czy hugepages są włączone w systemie Linux (oryginalny tutaj)

Problem:
Musisz sprawdzić, czy HugePages jest włączone w Twoim systemie.

rozwiązanie:
To całkiem proste:

cat /sys/kernel/mm/transparent_hugepage/enabled

Otrzymasz coś takiego:

always [madvise] never

Zobaczysz listę dostępnych opcji (zawsze, Madvise, nigdy), a aktualnie aktywna opcja zostanie ujęta w nawiasy (domyślnie Madvise).

Madvise Oznacza to, że transparent hugepages włączone tylko dla obszarów pamięci, które jawnie żądają użycia ogromnych stron Madvise(2).

zawsze Oznacza to, że transparent hugepages zawsze włączone dla wszystkich procesów. Zwykle poprawia to wydajność, ale jeśli masz przypadek użycia, w którym wiele procesów zużywa niewielką ilość pamięci, całkowite obciążenie pamięci może drastycznie wzrosnąć.

nigdy Oznacza to, że transparent hugepages nie zostaną uwzględnione, nawet jeśli zostaniesz o to poproszony za pomocą Madvise. Aby dowiedzieć się więcej skontaktuj się dokumentacja Jądra Linuksa.

Jak zmienić wartość domyślną

opcja 1: Bezpośrednia zmiana sysfs (po ponownym uruchomieniu parametr powró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/enabled

opcja 2: Zmień domyślne ustawienia systemu, rekompilując jądro ze zmodyfikowaną konfiguracją (ta opcja jest zalecana tylko wtedy, gdy używasz niestandardowego jądra):

  • Aby ustawić zawsze domyślnie, użyj:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Aby ustawić Madvise jako domyślny, użyj:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Część 2: Zalety i wady HugePages

Postaramy się wybiórczo wyjaśnić zalety, wady i możliwe pułapki korzystania z Hugepages. Ponieważ skomplikowany technologicznie i pedantyczny artykuł będzie prawdopodobnie trudny do zrozumienia dla ludzi, którzy łudzą się, że Hugepages to panaceum, poświęcę dokładność na rzecz prostoty. Warto tylko mieć na uwadze, że wiele tematów jest naprawdę skomplikowanych i dlatego znacznie uproszczonych.

Proszę zwrócić uwagę, że mówimy o 64-bitowych systemach x86 z systemem Linux i że po prostu zakładam, że system obsługuje przezroczyste ogromne strony (ponieważ nie jest wadą to, że ogromne strony nie są nadpisywane), jak ma to miejsce w prawie każdym nowoczesnym Linuksie środowisko.

Więcej opisu technicznego załączę w linkach poniżej.

Pamięć wirtualna

Jeśli jesteś programistą C++, wiesz, że obiekty w pamięci mają określone adresy (wartości wskaźników).

Jednakże adresy te niekoniecznie odzwierciedlają adresy fizyczne w pamięci (adresy RAM). Reprezentują adresy w pamięci wirtualnej. Procesor ma specjalny moduł MMU (jednostka zarządzania pamięcią), który pomaga jądru mapować pamięć wirtualną do lokalizacji fizycznej.

Takie podejście ma wiele zalet, ale najważniejsze z nich to:

  • Wydajność (z różnych powodów);
  • Izolacja programu, to znaczy żaden program nie może czytać z pamięci innego programu.

Czym są strony?

Pamięć wirtualna jest podzielona na strony. Każda pojedyncza strona wskazuje na konkretną pamięć fizyczną, może wskazywać obszar w pamięci RAM lub może wskazywać adres przypisany do urządzenia fizycznego, takiego jak karta graficzna.

Większość stron, z którymi się spotykasz, albo wskazuje na pamięć RAM, albo jest zamieniona, co oznacza, że ​​są przechowywane na Twoim dysku twardym lub dysku SSD. Jądro zarządza fizycznym układem każdej strony. W przypadku uzyskania dostępu do fałszywej strony jądro zatrzymuje wątek próbujący uzyskać dostęp do pamięci, wczytuje stronę z dysku twardego/SSD do pamięci RAM, a następnie kontynuuje wykonywanie wątku.

Proces ten jest przezroczysty dla strumienia, co oznacza, że ​​niekoniecznie odczytuje dane bezpośrednio z dysku twardego/SSD. Rozmiar normalnych stron wynosi 4096 bajtów. Rozmiar Hugepages wynosi 2 megabajty.

Bufor asocjacyjny tłumaczeniowy (TLB)

Kiedy program uzyskuje dostęp do strony pamięci, procesor musi wiedzieć, z której fizycznej strony odczytać dane (tzn. posiadać wirtualną mapę adresów).

Jądro ma strukturę danych (tabelę stron), która zawiera wszystkie informacje o używanych stronach. Korzystając z tej struktury danych, można zmapować adres wirtualny na adres fizyczny.

Jednak tabela stron jest dość złożona i powolna, więc po prostu nie możemy analizować całej struktury danych za każdym razem, gdy proces uzyskuje dostęp do pamięci.

Na szczęście nasz procesor ma TLB, który buforuje mapowanie między adresami wirtualnymi i fizycznymi. Oznacza to, że chociaż musimy przeanalizować tabelę stron przy pierwszej próbie dostępu, wszystkie kolejne dostępy do strony mogą być obsługiwane w TLB, co pozwala na szybkie działanie.

Ponieważ jest zaimplementowany jako urządzenie fizyczne (co przede wszystkim sprawia, że ​​jest szybki), jego pojemność jest ograniczona. Jeśli więc chcesz uzyskać dostęp do większej liczby stron, TLB nie będzie w stanie przechowywać mapowań dla nich wszystkich, co spowoduje, że Twój program będzie działał znacznie wolniej.

Z pomocą przychodzą Hugepages

Co więc możemy zrobić, aby uniknąć przepełnienia TLB? (Zakładamy, że program nadal potrzebuje tej samej ilości pamięci).

Tutaj właśnie wkracza Hugepages. Zamiast 4096 bajtów wymagających tylko jednego wpisu TLB, jeden wpis TLB może teraz wskazywać aż 2 megabajty. Załóżmy, że TLB ma 512 wpisów, tutaj bez Hugepages możemy dopasować:

4096 b⋅512=2 MB

Jak zatem możemy się z nimi porównywać:

2 MB⋅512=1 GB

Właśnie dlatego Hugepages jest niesamowity. Mogą zwiększyć produktywność bez większego wysiłku. Ale są tu istotne zastrzeżenia.

Ogromne strony fałszują

Jądro automatycznie monitoruje częstotliwość używania każdej strony pamięci. Jeśli nie ma wystarczającej ilości pamięci fizycznej (RAM), jądro przeniesie mniej ważne (rzadziej używane) strony na dysk twardy, aby zwolnić trochę pamięci RAM dla ważniejszych stron.
W zasadzie to samo dotyczy Hugepages. Jednakże jądro może zamieniać tylko całe strony, a nie pojedyncze bajty.

Załóżmy, że mamy taki program:

char* mymemory = malloc(2*1024*1024); // Возьмем это за одну Hugepage!
// Заполним mymemory какими-либо данными
// Сделаем много других вещей,
// которые приведут к подмене страницы mymemory
// ...
// Запросим доступ только к первому байту
putchar(mymemory[0]); 

W tym przypadku jądro będzie musiało zastąpić (odczytać) aż 2 megabajty informacji z dysku twardego/SSD, abyś mógł odczytać jeden bajt. Jeśli chodzi o zwykłe strony, z dysku twardego/SSD należy odczytać tylko 4096 bajtów.

Dlatego też, jeśli ogromna strona zostanie nadpisana, czytanie będzie szybsze tylko wtedy, gdy trzeba uzyskać dostęp do całej strony. Oznacza to, że jeśli próbujesz uzyskać losowy dostęp do różnych części pamięci i czytasz tylko kilka kilobajtów, powinieneś używać zwykłych stron i nie martwić się o nic innego.

Z drugiej strony, jeśli potrzebujesz sekwencyjnego dostępu do dużej części pamięci, ogromne strony poprawią twoją wydajność. Musisz jednak przetestować to samodzielnie (nie za pomocą abstrakcyjnego oprogramowania) i zobaczyć, co działa szybciej.

Alokacja w pamięci

Jeśli napiszesz C, wiesz, że możesz zażądać dowolnie małej (lub prawie dowolnie dużej) ilości pamięci ze sterty za pomocą malloc(). Załóżmy, że potrzebujesz 30 bajtów pamięci:

char* mymemory = malloc(30);

Programiście może się wydawać, że „żądasz” od systemu operacyjnego 30 bajtów pamięci i zwracasz wskaźnik do jakiejś pamięci wirtualnej. Ale tak na prawdę malloc () to po prostu funkcja C, która wywołuje ją z wnętrza tej funkcji brk i sbrk aby zażądać lub zwolnić pamięć 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 wykorzystać. malloc() implementuje dość złożone algorytmy ponownego wykorzystania zwolnionej pamięci.

Jednocześnie wszystko dzieje się dla Ciebie niezauważone, więc dlaczego miałoby Cię to niepokoić? Ale ponieważ wyzwanie free() to nie znaczy pamięć jest koniecznie natychmiast zwracana do systemu operacyjnego.

Istnieje coś takiego jak fragmentacja pamięci. W skrajnych przypadkach istnieją segmenty sterty, w których wykorzystywanych jest tylko kilka bajtów, a wszystko pomiędzy nimi zostało zwolnione (free()).

Należy pamiętać, że fragmentacja pamięci to niezwykle złożony temat i nawet drobne zmiany w programie mogą mieć znaczący wpływ. W większości przypadków programy nie spowodują znacznej fragmentacji pamięci, jednak należy mieć świadomość, że jeśli w jakimś obszarze sterty pojawi się problem z fragmentacją, to ogromne strony mogą pogorszyć sytuację.

Selektywne wykorzystanie ogromnych stron

Po przeczytaniu tego artykułu ustaliłeś, które części Twojego programu mogą zyskać na korzystaniu z hugepages, a które nie. Czy zatem hugepages powinno być w ogóle włączone?

Na szczęście możesz użyć madvise()aby włączyć ogromne stronicowanie tylko dla tych obszarów pamięci, w których byłoby to przydatne.

Najpierw sprawdź, czy hugepages działa w trybie madvise(), używając instrukcje na początku artykułu.

Następnie użyj madvise()aby powiedzieć jądru dokładnie, gdzie ma używać 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)

Zauważ, że ta metoda jest po prostu radą dla jądra, jak zarządzać pamięcią. Nie oznacza to jednak, że jądro automatycznie użyje ogromnych stron dla danej pamięci.

Zapoznaj się z dokumentacją (strona podręcznika)madviseaby dowiedzieć się więcej o zarządzaniu pamięcią i madvise(), ten temat ma niezwykle stromą krzywą uczenia się. Jeśli więc chcesz być w tym naprawdę dobry, przygotuj się na czytanie i testowanie przez kilka tygodni, zanim spodziewasz się pozytywnych rezultatów.

Co czytać?

Mam pytanie? Napisz w komentarzach!

Źródło: www.habr.com

Dodaj komentarz