
Eine Übersetzung des Artikels wurde für Studenten des Linux Administrator- Kurses vorbereitet.
Zuvor habe ich darüber gesprochen, wie man die Verwendung von Hugepages unter Linux testet und aktiviert.
Dieser Artikel ist nur nützlich, wenn Sie wirklich wissen, wo Sie Hugepages verwenden können. Ich habe viele Leute getroffen, die von der Aussicht getäuscht werden, dass Hugepages die Produktivität auf magische Weise steigern wird. Riesiges Paging ist jedoch ein komplexes Thema und kann bei unsachgemäßer Verwendung die Leistung beeinträchtigen.
Teil 1: Vergewissern Sie sich, dass unter Linux riesige Seiten enthalten sind (Original hier )
Problem:
Sie müssen überprüfen, ob HugePages auf Ihrem System aktiviert sind.
Lösung:
Es ist ziemlich einfach:
cat /sys/kernel/mm/transparent_hugepage/enabled
Sie werden so etwas bekommen:
always [madvise] never
Sie sehen eine Liste der verfügbaren Optionen ( immer, madvise, nie ), während die aktuell aktive Option in Klammern steht (standardmäßig madvise ).
madvise bedeutet, dass transparent hugepages
nur für Speicherbereiche enthalten sind, die explizit Riesenseiten mit madvise anfordern (2) .
bedeutet immer, dass transparent hugepages
immer für alle Prozesse transparent hugepages
sind. Dies verbessert normalerweise die Leistung. Wenn Sie jedoch einen Anwendungsfall haben, in dem viele Prozesse wenig Speicher verbrauchen, kann sich die Gesamtspeicherlast dramatisch erhöhen.
bedeutet niemals, dass transparent hugepages
nicht enthalten sind, selbst wenn sie mit madvise angefordert werden. Weitere Informationen finden Sie in der Dokumentation zum Linux-Kernel.
So ändern Sie den Standardwert
Option 1 : Ändern Sie sysfs
direkt (nach dem Neustart wird der Parameter 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 Systemstandard, indem Sie den Kernel mit der geänderten Konfiguration neu kompilieren (diese Option wird nur empfohlen, wenn Sie Ihren eigenen Kernel verwenden):
Teil 2: Vor- und Nachteile von HugePages
Wir werden versuchen, die Vor- und Nachteile und möglichen Fehler bei der Verwendung von Hugepages selektiv zu erklären. Da der technologisch anspruchsvolle und pedantische Artikel für Menschen, die getäuscht werden, weil sie Hugepages als Allheilmittel betrachten, wahrscheinlich schwierig ist, werde ich der Einfachheit halber auf Genauigkeit verzichten. Es ist nur zu bedenken, dass viele Themen sehr komplex und daher stark vereinfacht sind.
Bitte beachten Sie, dass es sich um 64-Bit-x86-Systeme handelt, die unter Linux ausgeführt werden, und dass ich nur davon ausgehe, dass das System transparente riesige Seiten unterstützt (da es kein Nachteil ist, dass riesige Seiten nicht ersetzt werden), wie dies bei fast allen modernen Systemen der Fall ist Linux-Umgebung.
In den unten stehenden Links werde ich eine weitere technische Beschreibung 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 (Adressen im RAM) wider. Sie sind Adressen im virtuellen Speicher. Der Prozessor verfügt über ein spezielles MMU-Modul (Memory Management Unit), mit dessen Hilfe der Kernel den virtuellen Speicher einem physischen Standort zuordnen kann.
Dieser Ansatz hat viele Vorteile, aber die grundlegendsten:
- Leistung (aus verschiedenen Gründen);
- Isolierung von Programmen, dh keines der Programme 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, kann auf einen Bereich im RAM verweisen oder auf eine Adresse, die einem physischen Gerät wie einer Grafikkarte zugewiesen ist.
Die meisten Seiten, mit denen Sie sich befassen, verweisen entweder auf RAM oder Swap, dh sie werden auf Ihrer Festplatte oder SSD gespeichert. Der Kernel steuert 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 den Thread dann weiter aus.
Dieser Prozess ist für den Stream transparent, dh er liest nicht unbedingt direkt von der Festplatte / SSD. Die Größe normaler Seiten beträgt 4096 Byte. Riesenseiten sind 2 Megabyte groß.
Assoziativer Übersetzungspuffer (TLB)
Wenn ein Programm auf eine Speicherseite zugreift, muss der Zentralprozessor wissen, von welcher physischen Seite Daten gelesen werden sollen (d. H. Eine virtuelle Adresszuordnung haben).
Der Kern hat eine Datenstruktur (Seitentabelle), die alle Informationen zu den verwendeten Seiten enthält. Mit dieser Datenstruktur können Sie eine virtuelle Adresse einer physischen Adresse zuordnen.
Die Seitentabelle ist jedoch ziemlich komplex und läuft 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 von virtuellen und physischen Adressen zwischenspeichert. Dies bedeutet, dass trotz der Tatsache, dass wir die Seitentabelle beim ersten Versuch, Zugriff zu erhalten, analysieren müssen, alle nachfolgenden Seitenaufrufe im TLB verarbeitet werden können, was einen schnellen Betrieb gewährleistet.
Da es als physisches Gerät implementiert ist (was es hauptsächlich schnell macht), ist seine Kapazität begrenzt. Wenn Sie auf mehr Seiten zugreifen möchten, kann TLB die Zuordnung daher nicht für alle Seiten speichern, wodurch Ihr Programm viel langsamer arbeitet.
Riesenseiten kommen zur Rettung
Was können wir also tun, um einen TLB-Überlauf zu vermeiden? (Wir gehen davon aus, dass das Programm immer noch dieselbe Speichermenge benötigt).
Hier erscheinen die Hugepages. Anstelle von 4096 Bytes, für die nur ein Eintrag im TLB erforderlich ist, kann ein Eintrag im TLB jetzt auf satte 2 Megabyte verweisen. Wir gehen davon aus, dass der TLB 512 Einträge hat, hier ohne riesige Seiten können wir übereinstimmen:
4096 b⋅512=2 MB
Mit ihnen können wir vergleichen:
2 MB⋅512=1 GB
Deshalb ist Hugepages großartig. Sie können die Produktivität ohne erheblichen Aufwand steigern. Es gibt jedoch erhebliche Vorbehalte.
Riesige Spoofing-Seiten
Der Kernel verfolgt automatisch die Nutzungshäufigkeit jeder Speicherseite. Wenn der physische Speicher (RAM) nicht ausreicht, verschiebt der Kernel die weniger wichtigen (weniger häufig verwendeten) Seiten auf die Festplatte, um einen Teil des RAM für wichtigere Seiten freizugeben.
Grundsätzlich gilt das Gleiche für Hugepages. Der Kernel kann jedoch nur ganze Seiten austauschen, nicht einzelne Bytes.
Angenommen, 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), damit Sie ein Byte lesen können. Wie bei normalen Seiten müssen nur 4096 Bytes von der Festplatte / SSD gelesen werden.
Wenn eine große Seite ersetzt wird, ist das Lesen daher nur dann schneller, wenn Sie auf die gesamte Seite zugreifen müssen. Dies bedeutet, dass Sie, wenn Sie versuchen, zufällig auf verschiedene Teile des Speichers zuzugreifen und nur ein paar Kilobyte zu lesen, normale Seiten verwenden und sich um nichts anderes kümmern sollten.
Wenn Sie jedoch nacheinander auf den größten Teil des Speichers zugreifen müssen, erhöhen riesige Seiten Ihre Produktivität. Sie müssen dies jedoch selbst überprüfen (und nicht am Beispiel einer abstrakten Software) und feststellen, was schneller funktioniert.
Speicherzuordnung
Wenn Sie in C schreiben, wissen Sie, dass Sie mit malloc()
beliebig kleine (oder fast beliebig große) Speichermengen vom Heap anfordern können. Angenommen, Sie benötigen 30 Byte Speicher:
char* mymemory = malloc(30);
Dem Programmierer scheint es, als würden Sie 30 Byte Speicher vom Betriebssystem "anfordern" und einen Zeiger auf einen virtuellen Speicher zurückgeben. Tatsächlich ist malloc ()
jedoch nur eine C-Funktion, die die Funktionen brk und sbrk von innen aufruft, um Speicher vom Betriebssystem anzufordern oder freizugeben .
Das Anfordern von immer mehr Speicher für jede Zuordnung ist jedoch ineffizient. Es ist sehr wahrscheinlich, dass ein Speichersegment bereits freigegeben wurde (free())
, und wir können es wiederverwenden. malloc()
implementiert ziemlich komplexe Algorithmen zur Wiederverwendung von freigegebenem Speicher.
Gleichzeitig passiert für Sie alles unbemerkt. Warum sollte es Sie also betreffen? Da der Aufruf von free()
jedoch nicht bedeutet, dass der Speicher sofort an das Betriebssystem zurückgegeben wird .
Es gibt so etwas wie Speicherfragmentierung. In extremen Fällen gibt es Heap-Segmente, in denen nur wenige Bytes verwendet werden, während alles dazwischen freigegeben wurde (free())
.
Beachten Sie, dass die Speicherfragmentierung ein unglaublich komplexes Thema ist und selbst geringfügige Änderungen am Programm erhebliche Auswirkungen haben können. In den meisten Fällen verursachen Programme keine signifikante Speicherfragmentierung. Beachten Sie jedoch, dass große Seiten die Situation nur verschlimmern können, wenn in einem bestimmten Bereich des Heapspeichers eine Fragmentierung auftritt.
Benutzerdefinierte Anwendung von riesigen Seiten
Nach dem Lesen des Artikels haben Sie festgestellt, welche Teile Ihres Programms von der Verwendung großer Seiten profitieren können und welche nicht. Sollten also überhaupt riesige Seiten enthalten sein?
Glücklicherweise können Sie madvise()
, um Riesenseiten nur für Speicherbereiche zu aktivieren, in denen dies nützlich ist.
Stellen Sie zunächst sicher, dass riesige Seiten im madvise () -Modus funktionieren. Befolgen Sie dazu die Anweisungen am Anfang des Artikels.
Verwenden Sie dann madvise()
, um dem Kernel genau mitzuteilen, wo riesige Seiten verwendet 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)
Beachten Sie, dass diese Methode nur eine Empfehlung an den Kernel für die Speicherverwaltung ist. Dies bedeutet nicht, dass der Kernel automatisch riesige Seiten für den angegebenen Speicher verwendet.
Weitere Informationen zur Speicherverwaltung und zu madvise()
, einer unglaublich steilen Lernkurve für dieses Thema, finden Sie auf der madvise-Manpage . Wenn Sie es wirklich gut verstehen möchten, machen Sie sich bereit, es einige Wochen lang zu lesen und zu testen, bevor Sie mit mindestens einem positiven Ergebnis rechnen.
Was zu lesen?
Hast du eine Frage? Schreiben Sie in die Kommentare!