Vorteile und Nachteile von HugePages

Vorteile und Nachteile von HugePages

Die Übersetzung des Artikels wurde für die Studierenden des Kurses vorbereitet „Linux-Administrator“.

Wie man zuvor erläutert hat, wie man Hugepages in Linux überprüft und aktiviert.
Dieser Artikel ist nur dann nützlich, wenn Sie tatsächlich einen Anwendungsfall für Hugepages haben. Ich habe viele Leute getroffen, die von der Aussicht getäuscht werden, dass Hugepages die Leistung auf magische Weise steigern. Dennoch ist Hugepaging ein komplexes Thema, und bei falscher Anwendung kann die Leistung sinken.

Teil 1: Überprüfen, ob Hugepages in Linux aktiviert sind (Original hier)

Problem:
Es muss überprüft werden, ob HugePages in Ihrem System aktiviert sind.

Lösung:
Es ist ziemlich einfach:

cat /sys/kernel/mm/transparent_hugepage/enabled

Sie erhalten etwas in der Art:

always [madvise] never

Sie sehen eine Liste verfügbarer Optionen (always, madvise, never), wobei die aktuell aktive Option in Klammern gesetzt ist (Standardmäßig madvise).

madvise bedeutet, dass transparente Hugepages sind nur für Speicherbereiche aktiviert, die explizit Hugepages mit Hilfe von madvise(2).

always bedeutet, dass transparente Hugepages immer und für alle Prozesse aktiviert. In der Regel verbessert dies die Leistung, aber wenn Sie einen Anwendungsfall haben, in dem viele Prozesse eine kleine Menge Speicher verbrauchen, kann die Gesamtspeicherauslastung stark ansteigen.

never bedeutet, dass transparente Hugepages werden selbst bei einer Anfrage mit madvise nicht aktiviert. Um mehr zu erfahren, verweisen Sie auf Dokumentation den Linux-Kernel.

Wie man den Standardwert ändert

Option 1: Direkt ändern sysfs (nach einem Neustart wird die Einstellung auf den Standardwert zurückgesetzt):

echo always > /sys/kernel/mm/transparent_hugepage/enabled
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/enabled

Option 2: Ändern Sie den Standardwert des Systems, indem Sie den Kernel mit einer geänderten Konfiguration neu kompilieren (diese Option wird nur empfohlen, wenn Sie einen eigenen Kernel verwenden):

  • Um always als Standard zu setzen, verwenden Sie:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Kommentieren Sie CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y aus
  • Um madvise als Standard zu setzen, verwenden Sie:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Kommentieren Sie CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y aus

Teil 2: Vorteile und Nachteile von HugePages

Wir werden versuchen, die Vorteile, Nachteile und häufigen Fehler bei der Verwendung von Hugepages selektiv zu erläutern. Da es sich um einen technologisch komplexen und detaillierten Artikel handelt, wird es wahrscheinlich schwer sein, ihn zu verstehen, besonders für diejenigen, die fälschlicherweise glauben, dass Hugepages ein Allheilmittel sind; ich werde daher die Genauigkeit zugunsten der Einfachheit opfern. Es ist nur wichtig zu beachten, dass viele Themen in der Tat komplex sind und daher stark vereinfacht werden.

Bitte beachten Sie, dass wir über 64-Bit-x86-Systeme sprechen, die unter Linux laufen, und dass ich einfach annehme, dass das System transparent hugepages unterstützt (da es kein Mangel ist, wenn hugepages nicht umgeschichtet werden), wie es in nahezu jeder modernen Linux-Umgebung der Fall ist.

In den Links unten werde ich mehr technische Beschreibungen anhängen.

Virtueller Speicher

Wenn Sie C++-Programmierer sind, wissen Sie, dass Objekte im Speicher spezifische Adressen (Pointer-Werte) haben.

Diese Adressen spiegeln jedoch nicht unbedingt die physischen Adressen im Speicher (Adressen im RAM) wider. Sie sind Adressen im virtuellen Speicher. Der Prozessor verfügt über ein spezielles Modul, die MMU (Memory Management Unit), die dem Kern hilft, den virtuellen Speicher mit dem physischen Standort abzugleichen.

Dieser Ansatz bietet viele Vorteile, aber die grundlegendsten sind:

  • Leistung (aus verschiedenen Gründen);
  • Isolation der Programme, das heißt, kein Programm kann den Speicher eines anderen Programms lesen.

Was sind Seiten?

Der virtuelle Speicher ist in Seiten unterteilt. Jede einzelne Seite zeigt auf einen bestimmten physischen Speicher, sie kann auf einen Bereich im RAM zeigen oder auf eine Adresse, die einem physischen Gerät zugewiesen ist, beispielsweise der Grafikkarte.

Die meisten Seiten, mit denen Sie es zu tun haben, zeigen entweder auf den RAM oder sind umgeschichtet (swap), das heißt, sie werden auf der Festplatte oder SSD gespeichert. Der Kern verwaltet den physischen Standort jeder Seite. Wenn auf eine umgeschichte Seite zugegriffen wird, stoppt der Kern den Thread, der versucht, auf den Speicher zuzugreifen, liest die Seite von der Festplatte/SSD in den RAM und setzt dann die Ausführung des Threads fort.

Dieser Prozess ist für den Thread transparent, das heißt, er liest nicht unbedingt direkt von der Festplatte/SSD. Die Größe normaler Seiten beträgt 4096 Bytes. Die Größe der Hugepages beträgt 2 Megabyte.

Translation Lookaside Buffer (TLB)

Wenn ein Programm auf eine bestimmte Seite des Speichers zugreift, muss der Prozessor wissen, von welcher physischen Seite die Daten gelesen werden sollen (d.h. eine virtuelle Adresskarte haben).

Im Kern gibt es eine Datenstruktur (seitentabelle), die alle Informationen über die verwendeten Seiten enthält. Mit dieser Datenstruktur lässt sich eine virtuelle Adresse mit einer physischen Adresse abgleichen.

Allerdings ist die Seitentabelle ziemlich komplex und arbeitet langsam, weshalb wir nicht jedes Mal die gesamte Datenstruktur analysieren können, wenn ein Prozess auf den Speicher zugreift.

Zum Glück verfügt unser Prozessor über einen TLB, der die Zuordnung von virtuellen zu physischen Adressen cached. Das bedeutet, dass wir die Seitentabelle bei jedem ersten Zugriff analysieren müssen, aber alle nachfolgenden Zugriffe auf die Seite im TLB bearbeitet werden können, was schnelle Operationen ermöglicht.

Da es als physisches Gerät implementiert ist (was es in erster Linie schnell macht), ist seine Kapazität begrenzt. Wenn Sie also auf mehr Seiten zugreifen möchten, kann der TLB die Zuordnung für all diese nicht speichern, weshalb Ihr Programm viel langsamer laufen wird.

Hugepages kommen zur Rettung

Also, was können wir tun, um eine TLB-Überlastung zu vermeiden? (Wir nehmen an, dass das Programm weiterhin denselben Speicherbedarf hat).

Hier kommen die Hugepages ins Spiel. Anstatt 4096 Byte zu benötigen, die nur einen Eintrag im TLB erfordern, kann jetzt ein TLB-Eintrag kolossale 2 Megabyte anzeigen. Nehmen wir an, der TLB hat 512 Einträge, ohne Hugepages könnten wir folgende Zuordnung erreichen:

4096 b⋅512=2 MB

Wohingegen wir mit ihnen folgende Zuordnung erreichen können:

2 MB⋅512=1 GB

Genau darum sind Hugepages großartig. Sie können die Leistung steigern, ohne signifikante Anstrengungen zu erfordern. Aber es gibt dabei wesentliche Einschränkungen.

Tausch von Hugepages

Der Kernel verfolgt automatisch die Nutzungsfrequenz jeder Speicherseite. Wenn nicht genügend physischer Speicher (RAM) vorhanden ist, wird der Kernel weniger wichtige (seltener verwendete) Seiten auf die Festplatte verschieben, um RAM für wichtigere Seiten freizugeben.
Im Prinzip gilt das Gleiche für Hugepages. Der Kernel kann jedoch nur ganze Seiten, nicht einzelne Bytes hin- und herschieben.

Nehmen wir an, wir haben folgendes Programm:

char* mymemory = malloc(2*1024*1024); // Betrachten wir dies als eine Hugepage! 
// Füllen wir mymemory mit einigen Daten 
// Machen wir viele andere Dinge, 
// die zum Tausch der Seite mymemory führen 
// ...
// Fordern wir den Zugriff nur auf das erste Byte an 
putchar(mymemory[0]); 

In diesem Fall muss der Kernel ganze 2 Megabyte Informationen von der Festplatte/SSD lesen, nur damit Sie ein Byte lesen können. Bei normalen Seiten hingegen muss nur 4096 Byte von der Festplatte/SSD gelesen werden.

Wenn hugepages also umgeschaltet werden, geschieht das Lesen schneller, aber nur, wenn Sie auf die gesamte Seite zugreifen müssen. Das bedeutet, dass Sie, wenn Sie zufällig auf verschiedene Teile des Speichers zugreifen und nur ein paar Kilobyte lesen, normale Seiten verwenden sollten und sich um nichts weiter kümmern müssen.

Andererseits, wenn Sie auf einen großen Teil des Speichers sequenziell zugreifen müssen, werden hugepages Ihre Leistung steigern. Sie müssen dies jedoch selbst überprüfen (und nicht anhand einer abstrakten Software) und sehen, was schneller funktioniert.

Speicherallokation

Wenn Sie in C programmieren, wissen Sie, dass Sie beliebig kleine (oder fast beliebig große) Speicherbereiche heap anfordern können mit malloc(). Angenommen, Sie benötigen 30 Byte Speicher:

char* mymemory = malloc(30);

Der Programmierer könnte denken, dass Sie 30 Byte Speicher vom Betriebssystem „anfordern“ und einen Zeiger auf einen bestimmten virtuellen Speicher zurückgeben. In Wirklichkeit jedoch malloc () ist einfach eine C-Funktion, die intern die Funktionen brk und sbrk aufruft, um Speicher vom Betriebssystem anzufordern oder freizugeben.

Es ist jedoch ineffizient, für jede Allokation immer mehr Speicher anzufordern; es ist wahrscheinlich, dass ein bestimmter Speicherbereich bereits freigegeben wurde (free()), und wir können ihn wiederverwenden. malloc() implementiert ziemlich komplexe Algorithmen zur Wiederverwendung freigegebener Speicher.

Das passiert für Sie alles unmerklich, also warum sollte es Sie kümmern? Weil der Aufruf free() nicht bedeutet, dass der Speicher sofort zurück an das Betriebssystem gegeben wird..

Es gibt ein Konzept namens Speicherfragmentierung. In extremen Fällen gibt es Heap-Segmente, in denen nur einige Bytes verwendet werden, während alles dazwischen freigegeben wurde. (free()).

Bitte beachten Sie, dass die Fragmentierung von Speicher ein unglaublich komplexes Thema ist und selbst geringfügige Änderungen im Programm erheblichen Einfluss darauf haben können. In den meisten Fällen führen Programme nicht zu einer signifikanten Speicherfragmentierung, aber Sie sollten bedenken, dass, wenn es in einem bestimmten Bereich des Heaps ein Problem mit der Fragmentierung gibt, Hugepages die Situation nur verschlimmern können.

Selektive Anwendung von Hugepages

Nachdem Sie den Artikel gelesen haben, haben Sie festgestellt, welche Teile Ihres Programms von der Anwendung von Hugepages profitieren können und welche nicht. Sollten Sie Hugepages also überhaupt aktivieren?

Glücklicherweise können Sie madvise(), um Hugepaging nur für die Bereiche des Speichers zu aktivieren, in denen es vorteilhaft ist.

Zuerst überprüfen Sie, ob Hugepages im madvise()-Modus arbeiten, indem Sie Anleitung zu Beginn des Artikels.

Verwenden Sie dann madvise(), um dem Kernel anzugeben, wo genau Hugepages eingesetzt werden sollen.

#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)

Bitte beachten Sie, dass diese Methode lediglich Empfehlungen an den Kernel zur Speicherverwaltung darstellt. Das bedeutet nicht, dass der Kernel automatisch Hugepages für den angegebenen Speicher nutzen wird.

Konsultieren Sie die Dokumentation (manpage) madvise, um mehr über die Speicherverwaltung zu erfahren und madvise(), diese Thematik hat eine unglaublich steile Lernkurve. Daher sollten Sie sich darauf vorbereiten, mehrere Wochen zu lesen und zu testen, bevor Sie mit irgendeinem positiven Ergebnis rechnen.

Was zu lesen?

Haben Sie eine Frage? Schreiben Sie in die Kommentare!

Quelle: habr.com

60GB SSD 8Gb DDR4