Vor- und Nachteile von HugePages

Vor- und Nachteile von HugePages

Übersetzung des Artikels für Kursteilnehmer „Linux-Administrator“.

Zuvor habe ich darüber gesprochen, wie man Hugepages unter Linux testet und aktiviert.
Dieser Artikel ist nur dann nützlich, wenn Sie tatsächlich einen Ort haben, an dem Sie Hugepages verwenden können. Ich habe viele Leute getroffen, die sich von der Aussicht täuschen lassen, dass Hugepages die Produktivität auf magische Weise steigern wird. Allerdings ist HugePaging ein komplexes Thema und kann bei falscher Anwendung zu Leistungseinbußen führen.

Teil 1: Überprüfen, ob Hugepages unter Linux aktiviert ist (Original). hier)

Problem:
Sie müssen überprüfen, ob HugePages auf Ihrem System aktiviert ist.

Lösung:
Es ist ganz einfach:

cat /sys/kernel/mm/transparent_hugepage/enabled

Sie erhalten so etwas:

always [madvise] never

Sie sehen eine Liste der verfügbaren Optionen (immer, madvise, nie), und die aktuell aktive Option wird in Klammern eingeschlossen (standardmäßig). verrückt).

verrückt bedeutet, dass transparent hugepages Nur für Speicherbereiche aktiviert, die explizit riesige Seiten anfordern madvise(2).

immer bedeutet, dass transparent hugepages immer für alle Prozesse aktiviert. Dies verbessert normalerweise die Leistung, aber wenn Sie einen Anwendungsfall haben, bei dem viele Prozesse nur wenig Speicher verbrauchen, kann die Gesamtspeicherauslastung dramatisch ansteigen.

hört niemals bedeutet, dass transparent hugepages werden auch bei Anfrage über madvise nicht berücksichtigt. Um mehr zu erfahren, wenden Sie sich an Dokumentation Linux-Kernel.

So ändern Sie den Standardwert

Option 1: Direkt ändern sysfs (Nach dem Neustart kehrt der Parameter auf seinen Standardwert zurück):

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 Systemstandard, indem Sie den Kernel mit einer geänderten Konfiguration neu kompilieren (diese Option wird nur empfohlen, wenn Sie einen benutzerdefinierten Kernel verwenden):

  • Um immer als Standard festzulegen, verwenden Sie:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Um Madvise als Standard festzulegen, verwenden Sie:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Teil 2: Vor- und Nachteile von HugePages

Wir werden versuchen, die Vorteile, Nachteile und möglichen Fallstricke der Nutzung von Hugepages punktuell zu erläutern. Da ein technologisch komplexer und pedantischer Artikel für Leute, die der Illusion verfallen sind, Hugepages sei ein Allheilmittel, wahrscheinlich schwer zu verstehen sein wird, werde ich Genauigkeit zugunsten der Einfachheit opfern. Es ist nur wichtig zu bedenken, dass viele Themen sehr komplex und daher stark vereinfacht sind.

Bitte beachten Sie, dass es sich um 64-Bit-x86-Systeme mit Linux handelt und ich einfach davon ausgehe, dass das System transparente Hugepages unterstützt (da es kein Nachteil ist, dass Hugepages nicht überschrieben werden), wie es bei fast jedem modernen Linux der Fall ist Umfeld.

Weitere technische Beschreibungen werde ich in den folgenden Links anhängen.

Virtueller Speicher

Wenn Sie ein C++-Programmierer sind, wissen Sie, dass Objekte im Speicher bestimmte Adressen (Zeigerwerte) haben.

Diese Adressen spiegeln jedoch nicht unbedingt die physischen Adressen im Speicher (RAM-Adressen) wider. Sie repräsentieren Adressen im virtuellen Speicher. Der Prozessor verfügt über ein spezielles MMU-Modul (Memory Management Unit), das dem Kernel dabei hilft, virtuellen Speicher einem physischen Speicherort zuzuordnen.

Dieser Ansatz hat viele Vorteile, die wichtigsten sind jedoch:

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

Was sind Seiten?

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

Die meisten Seiten, mit denen Sie sich befassen, verweisen entweder auf RAM oder sind ausgelagert, d. h. sie werden auf Ihrer Festplatte oder SSD gespeichert. Der Kernel verwaltet das physische Layout jeder Seite. Wenn auf eine gefälschte Seite zugegriffen wird, stoppt der Kernel den Thread, der versucht, auf den Speicher zuzugreifen, liest die Seite von der Festplatte/SSD in den RAM und fährt dann mit der Ausführung des Threads fort.

Dieser Prozess ist Stream-transparent, d. h. er liest nicht unbedingt direkt von der Festplatte/SSD. Die Größe normaler Seiten beträgt 4096 Byte. Die Größe der Hugepages beträgt 2 Megabyte.

Translationsassoziativer Puffer (TLB)

Wenn ein Programm auf eine Speicherseite zugreift, muss die CPU wissen, von welcher physischen Seite sie Daten lesen soll (d. h. über eine virtuelle Adresszuordnung verfügen).

Der Kernel verfügt über eine Datenstruktur (Seitentabelle), die alle Informationen zu den verwendeten Seiten enthält. Mithilfe dieser Datenstruktur können Sie eine virtuelle Adresse einer physischen Adresse zuordnen.

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

Glücklicherweise verfügt unser Prozessor über einen TLB, der die Zuordnung zwischen virtuellen und physischen Adressen zwischenspeichert. Dies bedeutet, dass wir zwar die Seitentabelle beim ersten Zugriffsversuch analysieren müssen, alle nachfolgenden Zugriffe auf die Seite jedoch im TLB verarbeitet werden können, was einen schnellen Betrieb 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 nicht für alle Seiten Zuordnungen speichern, wodurch Ihr Programm viel langsamer läuft.

Hugepages kommt zur Rettung

Was können wir also tun, um einen TLB-Überlauf zu vermeiden? (Wir gehen davon aus, dass das Programm immer noch die gleiche Menge an Speicher benötigt).

Hier kommt Hugepages ins Spiel. Statt 4096 Bytes, die nur einen TLB-Eintrag erfordern, kann ein TLB-Eintrag jetzt auf satte 2 Megabyte verweisen. Nehmen wir an, der TLB hat 512 Einträge, hier können wir ohne Hugepages einen Abgleich durchführen:

4096 b⋅512=2 MB

Wie können wir dann mit ihnen vergleichen:

2 MB⋅512=1 GB

Deshalb ist Hugepages großartig. Sie können die Produktivität ohne großen Aufwand steigern. Hier gibt es jedoch erhebliche Vorbehalte.

Riesiges Seiten-Spoofing

Der Kernel überwacht automatisch, wie oft jede Speicherseite verwendet wird. Wenn nicht genügend physischer Speicher (RAM) vorhanden ist, verschiebt der Kernel weniger wichtige (weniger häufig verwendete) Seiten auf die Festplatte, um etwas RAM für wichtigere Seiten freizugeben.
Im Prinzip gilt das Gleiche auch für Hugepages. Allerdings kann der Kernel nur ganze Seiten austauschen, nicht einzelne Bytes.

Nehmen wir an, wir haben ein Programm wie dieses:

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

In diesem Fall muss der Kernel bis zu 2 Megabyte an Informationen von der Festplatte/SSD ersetzen (lesen), nur damit Sie ein Byte lesen können. Bei regulären Seiten müssen nur 4096 Byte von der Festplatte/SSD gelesen werden.

Wenn Hugepage überschrieben wird, ist das Lesen daher nur dann schneller, wenn Sie auf die gesamte Seite zugreifen müssen. Das heißt, wenn Sie versuchen, zufällig auf verschiedene Teile des Speichers zuzugreifen und nur ein paar Kilobyte lesen, sollten Sie normale Seiten verwenden und sich um nichts anderes kümmern.

Wenn Sie andererseits nacheinander auf einen großen Teil des Speichers zugreifen müssen, verbessern Hugepages Ihre Leistung. Sie müssen es jedoch selbst testen (nicht mit abstrakter Software) und sehen, was schneller funktioniert.

Zuordnung im Speicher

Wenn Sie C schreiben, wissen Sie, dass Sie mit beliebig kleine (oder fast beliebig große) Speichermengen vom Heap anfordern können malloc(). Nehmen wir an, Sie benötigen 30 Byte Speicher:

char* mymemory = malloc(30);

Für einen Programmierer könnte es so aussehen, als würden Sie 30 Byte Speicher vom Betriebssystem „anfordern“ und einen Zeiger auf einen virtuellen Speicher zurückgeben. Aber in Wirklichkeit malloc () ist nur eine C-Funktion, die aus der Funktion heraus aufgerufen wird brk und sbrk um Speicher vom Betriebssystem anzufordern oder freizugeben.

Allerdings ist es ineffizient, für jede Zuweisung immer mehr Speicher anzufordern. Höchstwahrscheinlich wurde bereits ein Speichersegment freigegeben (free()), und wir können es wiederverwenden. malloc() implementiert recht komplexe Algorithmen zur Wiederverwendung von freigegebenem Speicher.

Gleichzeitig passiert für Sie alles unbemerkt, warum sollte es Sie also beunruhigen? Aber weil die Herausforderung free() bedeutet das nicht Der Speicher wird zwangsläufig sofort an das Betriebssystem zurückgegeben.

Es gibt so etwas wie Speicherfragmentierung. Im Extremfall gibt es Heap-Segmente, in denen nur wenige Bytes verwendet werden, während alles dazwischen freigegeben wurde (free()).

Bitte beachten Sie, dass Speicherfragmentierung ein unglaublich komplexes Thema ist und selbst geringfügige Änderungen an einem Programm erhebliche Auswirkungen haben können. In den meisten Fällen verursachen Programme keine nennenswerte Speicherfragmentierung. Sie sollten sich jedoch darüber im Klaren sein, dass große Seiten die Situation verschlimmern können, wenn in einem Bereich des Heaps ein Problem mit der Fragmentierung auftritt.

Gezielter Einsatz von Hugepages

Nachdem Sie diesen Artikel gelesen haben, haben Sie festgestellt, welche Teile Ihres Programms von der Verwendung von Hugepages profitieren können und welche nicht. Sollten Hugepages also überhaupt aktiviert werden?

Zum Glück können Sie verwenden madvise()um Hugepaging nur für die Speicherbereiche zu aktivieren, in denen es nützlich wäre.

Überprüfen Sie zunächst mithilfe von, ob Hugepages im madvise()-Modus ausgeführt wird Anleitung am Anfang des Artikels.

Dann benutze madvise()um dem Kernel genau mitzuteilen, wo Hugepages verwendet werden soll.

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

Beachten Sie, dass es sich bei dieser Methode lediglich um einen Hinweis für den Kernel zur Speicherverwaltung handelt. Dies bedeutet nicht, dass der Kernel automatisch Hugepages für einen bestimmten Speicher verwendet.

Siehe Dokumentation (Manpage)madviseum mehr über Speicherverwaltung und zu erfahren madvise(), dieses Thema hat eine unglaublich steile Lernkurve. Wenn Sie also beabsichtigen, wirklich gut darin zu werden, bereiten Sie sich darauf vor, einige Wochen lang zu lesen und zu testen, bevor Sie positive Ergebnisse erwarten.

Was soll ich lesen?

Eine Frage haben? Schreib in die Kommentare!

Source: habr.com

Kommentar hinzufügen