Πλεονεκτήματα και μειονεκτήματα του HugePages

Πλεονεκτήματα και μειονεκτήματα του HugePages

Μετάφραση του άρθρου που ετοιμάστηκε για φοιτητές του μαθήματος "Διαχειριστής Linux".

Προηγουμένως, μίλησα για τον τρόπο δοκιμής και ενεργοποίησης του Hugepages στο Linux.
Αυτό το άρθρο θα είναι χρήσιμο μόνο εάν έχετε πραγματικά ένα μέρος για να χρησιμοποιήσετε το Hugepages. Έχω γνωρίσει πολλούς ανθρώπους που ξεγελιούνται από την προοπτική ότι το Hugepages θα βελτιώσει μαγικά την παραγωγικότητα. Ωστόσο, η τεράστια σελιδοποίηση είναι ένα περίπλοκο θέμα και μπορεί να υποβαθμίσει την απόδοση εάν χρησιμοποιηθεί εσφαλμένα.

Μέρος 1: Επαλήθευση ότι οι τεράστιες σελίδες είναι ενεργοποιημένες στο Linux (πρωτότυπο εδώ)

Πρόβλημα:
Πρέπει να ελέγξετε εάν το HugePages είναι ενεργοποιημένο στο σύστημά σας.

λύση:
Είναι αρκετά απλό:

cat /sys/kernel/mm/transparent_hugepage/enabled

Θα πάρετε κάτι σαν αυτό:

always [madvise] never

Θα δείτε μια λίστα με τις διαθέσιμες επιλογές (πάντα, τρελός, ποτέ), και η τρέχουσα ενεργή επιλογή θα περικλείεται σε παρένθεση (από προεπιλογή τρελός).

τρελός σημαίνει ότι transparent hugepages ενεργοποιείται μόνο για περιοχές μνήμης που ζητούν ρητά τεράστιες σελίδες χρησιμοποιώντας madvise (2).

πάντοτε σημαίνει ότι transparent hugepages πάντα ενεργοποιημένο για όλες τις διαδικασίες. Αυτό συνήθως βελτιώνει την απόδοση, αλλά εάν έχετε μια περίπτωση χρήσης όπου πολλές διεργασίες καταναλώνουν μικρή ποσότητα μνήμης, τότε το συνολικό φορτίο μνήμης μπορεί να αυξηθεί δραματικά.

ποτέ σημαίνει ότι transparent hugepages δεν θα συμπεριληφθεί ακόμη και όταν ζητηθεί χρησιμοποιώντας το madvise. Για να μάθετε περισσότερα, επικοινωνήστε τεκμηρίωση Πυρήνες Linux.

Πώς να αλλάξετε την προεπιλεγμένη τιμή

Επιλογή 1: Άμεση αλλαγή sysfs (μετά την επανεκκίνηση η παράμετρος θα επιστρέψει στην προεπιλεγμένη τιμή):

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

Επιλογή 2: Αλλάξτε την προεπιλογή του συστήματος κάνοντας εκ νέου μεταγλώττιση του πυρήνα με μια τροποποιημένη διαμόρφωση (αυτή η επιλογή συνιστάται μόνο εάν χρησιμοποιείτε προσαρμοσμένο πυρήνα):

  • Για να ορίσετε πάντα από προεπιλογή, χρησιμοποιήστε:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Για να ορίσετε το madvise ως προεπιλογή, χρησιμοποιήστε:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Μέρος 2: Πλεονεκτήματα και μειονεκτήματα των HugePages

Θα προσπαθήσουμε να εξηγήσουμε επιλεκτικά τα πλεονεκτήματα, τα μειονεκτήματα και τις πιθανές παγίδες της χρήσης του Hugepages. Δεδομένου ότι ένα τεχνολογικά περίπλοκο και παιδαγωγικό άρθρο θα είναι πιθανότατα δύσκολο να γίνει κατανοητό για άτομα που παραπλανούνται ότι το Hugepages είναι πανάκεια, θα θυσιάσω την ακρίβεια για την απλότητα. Αξίζει απλώς να έχετε κατά νου ότι πολλά από τα θέματα είναι πραγματικά πολύπλοκα και επομένως πολύ απλοποιημένα.

Λάβετε υπόψη ότι μιλάμε για συστήματα 64-bit x86 που τρέχουν Linux και ότι απλώς υποθέτω ότι το σύστημα υποστηρίζει διαφανείς τεράστιες σελίδες (καθώς δεν είναι μειονέκτημα ότι οι τεράστιες σελίδες δεν αντικαθίστανται), όπως συμβαίνει σχεδόν σε κάθε σύγχρονο Linux περιβάλλον.

Θα επισυνάψω περισσότερη τεχνική περιγραφή στους παρακάτω συνδέσμους.

Εικονική μνήμη

Εάν είστε προγραμματιστής C++, γνωρίζετε ότι τα αντικείμενα στη μνήμη έχουν συγκεκριμένες διευθύνσεις (τιμές δείκτη).

Ωστόσο, αυτές οι διευθύνσεις δεν αντικατοπτρίζουν απαραίτητα φυσικές διευθύνσεις στη μνήμη (διευθύνσεις RAM). Αντιπροσωπεύουν διευθύνσεις στην εικονική μνήμη. Ο επεξεργαστής διαθέτει μια ειδική μονάδα MMU (μονάδα διαχείρισης μνήμης) που βοηθά τον πυρήνα να αντιστοιχίσει την εικονική μνήμη σε μια φυσική τοποθεσία.

Αυτή η προσέγγιση έχει πολλά πλεονεκτήματα, αλλά τα πιο σημαντικά είναι:

  • Απόδοση (για διάφορους λόγους).
  • Απομόνωση προγράμματος, δηλαδή κανένα πρόγραμμα δεν μπορεί να διαβάσει από τη μνήμη άλλου προγράμματος.

Τι είναι οι σελίδες;

Η εικονική μνήμη χωρίζεται σε σελίδες. Κάθε μεμονωμένη σελίδα δείχνει σε μια συγκεκριμένη φυσική μνήμη, μπορεί να δείχνει σε μια περιοχή στη μνήμη RAM ή μπορεί να δείχνει μια διεύθυνση που έχει εκχωρηθεί σε μια φυσική συσκευή, όπως μια κάρτα βίντεο.

Οι περισσότερες από τις σελίδες με τις οποίες ασχολείστε είτε οδηγούν στη μνήμη RAM είτε ανταλλάσσονται, δηλαδή αποθηκεύονται στον σκληρό σας δίσκο ή στο SSD. Ο πυρήνας διαχειρίζεται τη φυσική διάταξη κάθε σελίδας. Εάν γίνει πρόσβαση σε μια πλαστή σελίδα, ο πυρήνας σταματά το νήμα που προσπαθεί να αποκτήσει πρόσβαση στη μνήμη, διαβάζει τη σελίδα από τον σκληρό δίσκο/SSD στη μνήμη RAM και, στη συνέχεια, συνεχίζει την εκτέλεση του νήματος.

Αυτή η διαδικασία είναι διαφανής ροής, που σημαίνει ότι δεν διαβάζεται απαραίτητα απευθείας από το HDD/SSD. Το μέγεθος των κανονικών σελίδων είναι 4096 byte. Το μέγεθος των τεράστιων σελίδων είναι 2 megabyte.

Μεταφραστικό συσχετιστικό buffer (TLB)

Όταν ένα πρόγραμμα αποκτά πρόσβαση σε μια σελίδα μνήμης, η CPU πρέπει να γνωρίζει από ποια φυσική σελίδα να διαβάσει δεδομένα (δηλαδή, να έχει έναν εικονικό χάρτη διευθύνσεων).

Ο πυρήνας έχει μια δομή δεδομένων (πίνακας σελίδων) που περιέχει όλες τις πληροφορίες σχετικά με τις σελίδες που χρησιμοποιούνται. Χρησιμοποιώντας αυτήν τη δομή δεδομένων, μπορείτε να αντιστοιχίσετε μια εικονική διεύθυνση σε μια φυσική διεύθυνση.

Ωστόσο, ο πίνακας σελίδων είναι αρκετά περίπλοκος και αργός, επομένως απλά δεν μπορούμε να αναλύσουμε ολόκληρη τη δομή δεδομένων κάθε φορά που μια διεργασία αποκτά πρόσβαση στη μνήμη.

Ευτυχώς, ο επεξεργαστής μας διαθέτει ένα TLB που αποθηκεύει την αντιστοίχιση μεταξύ εικονικών και φυσικών διευθύνσεων. Αυτό σημαίνει ότι παρόλο που πρέπει να αναλύσουμε τον πίνακα σελίδων κατά την πρώτη προσπάθεια πρόσβασης, όλες οι επόμενες προσβάσεις στη σελίδα μπορούν να χειριστούν στο TLB, επιτρέποντας τη γρήγορη λειτουργία.

Επειδή υλοποιείται ως φυσική συσκευή (πράγμα που το κάνει γρήγορο στην αρχή), η χωρητικότητά του είναι περιορισμένη. Έτσι, εάν θέλετε να αποκτήσετε πρόσβαση σε περισσότερες σελίδες, το TLB δεν θα μπορεί να αποθηκεύσει αντιστοιχίσεις για όλες, με αποτέλεσμα το πρόγραμμά σας να τρέχει πολύ πιο αργά.

Ο Hugepages έρχεται στη διάσωση

Τι μπορούμε λοιπόν να κάνουμε για να αποφύγουμε την υπερχείλιση του TLB; (Υποθέτουμε ότι το πρόγραμμα χρειάζεται ακόμα την ίδια ποσότητα μνήμης).

Εδώ μπαίνει το Hugepages. Αντί για 4096 byte που απαιτούν μόνο μία καταχώρηση TLB, μία καταχώρηση TLB μπορεί τώρα να δείχνει τα τεράστια 2 megabyte. Ας υποθέσουμε ότι το TLB έχει 512 καταχωρήσεις, εδώ χωρίς Hugepages μπορούμε να ταιριάξουμε:

4096 b⋅512=2 MB

Τότε πώς μπορούμε να συγκρίνουμε μαζί τους:

2 MB⋅512=1 GB

Αυτός είναι ο λόγος που το Hugepages είναι υπέροχο. Μπορούν να βελτιώσουν την παραγωγικότητα χωρίς μεγάλη προσπάθεια. Αλλά υπάρχουν σημαντικές επιφυλάξεις εδώ.

Τεράστια πλαστογραφία

Ο πυρήνας παρακολουθεί αυτόματα πόσο συχνά χρησιμοποιείται κάθε σελίδα μνήμης. Εάν δεν υπάρχει αρκετή φυσική μνήμη (RAM), ο πυρήνας θα μετακινήσει λιγότερο σημαντικές (λιγότερο συχνά χρησιμοποιούμενες) σελίδες στον σκληρό δίσκο για να ελευθερώσει λίγη μνήμη RAM για πιο σημαντικές σελίδες.
Κατ' αρχήν, το ίδιο ισχύει και για το Hugepages. Ωστόσο, ο πυρήνας μπορεί να ανταλλάξει μόνο ολόκληρες σελίδες, όχι μεμονωμένα byte.

Ας υποθέσουμε ότι έχουμε ένα πρόγραμμα όπως αυτό:

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

Σε αυτήν την περίπτωση, ο πυρήνας θα χρειαστεί να αντικαταστήσει (διαβάσει) έως και 2 megabyte πληροφοριών από τον σκληρό δίσκο/SSD μόνο για να διαβάσετε ένα byte. Όσον αφορά τις κανονικές σελίδες, χρειάζεται να διαβαστούν μόνο 4096 byte από τον σκληρό δίσκο/SSD.

Επομένως, εάν η τεράστια σελίδα παρακαμφθεί, η ανάγνωση είναι ταχύτερη μόνο εάν χρειάζεται να αποκτήσετε πρόσβαση σε ολόκληρη τη σελίδα. Αυτό σημαίνει ότι εάν προσπαθείτε να αποκτήσετε τυχαία πρόσβαση σε διάφορα μέρη της μνήμης και διαβάζετε μόνο μερικά kilobyte, θα πρέπει να χρησιμοποιείτε κανονικές σελίδες και να μην ανησυχείτε για τίποτα άλλο.

Από την άλλη πλευρά, εάν χρειάζεται να αποκτήσετε διαδοχική πρόσβαση σε μεγάλο μέρος της μνήμης, οι τεράστιες σελίδες θα βελτιώσουν την απόδοσή σας. Ωστόσο, πρέπει να το δοκιμάσετε μόνοι σας (όχι με αφηρημένο λογισμικό) και να δείτε τι λειτουργεί πιο γρήγορα.

Κατανομή στη μνήμη

Εάν γράψετε C, γνωρίζετε ότι μπορείτε να ζητήσετε αυθαίρετα μικρές (ή σχεδόν αυθαίρετα μεγάλες) ποσότητες μνήμης από το σωρό χρησιμοποιώντας malloc(). Ας υποθέσουμε ότι χρειάζεστε 30 byte μνήμης:

char* mymemory = malloc(30);

Σε έναν προγραμματιστή, μπορεί να φαίνεται ότι «ζητάτε» 30 byte μνήμης από το λειτουργικό σύστημα και επιστρέφετε έναν δείκτη σε κάποια εικονική μνήμη. Αλλά στην πραγματικότητα malloc () είναι απλώς μια συνάρτηση C που καλεί μέσα από τη συνάρτηση brk και sbrk για να ζητήσετε ή να ελευθερώσετε μνήμη από το λειτουργικό σύστημα.

Ωστόσο, το να ζητάς όλο και περισσότερη μνήμη για κάθε εκχώρηση είναι αναποτελεσματικό. το πιθανότερο είναι ότι κάποιο τμήμα μνήμης έχει ήδη ελευθερωθεί (free()), και μπορούμε να το χρησιμοποιήσουμε ξανά. malloc() εφαρμόζει αρκετά σύνθετους αλγόριθμους για την επαναχρησιμοποίηση της ελευθερωμένης μνήμης.

Ταυτόχρονα, όλα γίνονται απαρατήρητα για εσάς, οπότε γιατί να σας ανησυχεί; Επειδή όμως η πρόκληση free() δεν σημαίνει αυτό Η μνήμη επιστρέφεται αναγκαστικά αμέσως στο λειτουργικό σύστημα.

Υπάρχει κάτι όπως κατακερματισμός μνήμης. Σε ακραίες περιπτώσεις, υπάρχουν τμήματα σωρού όπου χρησιμοποιούνται μόνο λίγα byte, ενώ όλα τα ενδιάμεσα έχουν απελευθερωθεί (free()).

Λάβετε υπόψη ότι ο κατακερματισμός της μνήμης είναι ένα απίστευτα περίπλοκο θέμα και ακόμη και μικρές αλλαγές σε ένα πρόγραμμα μπορεί να έχουν σημαντικό αντίκτυπο. Στις περισσότερες περιπτώσεις, τα προγράμματα δεν προκαλούν σημαντικό κατακερματισμό της μνήμης, αλλά θα πρέπει να γνωρίζετε ότι εάν υπάρχει πρόβλημα με κατακερματισμό σε κάποια περιοχή του σωρού, οι τεράστιες σελίδες μπορεί να επιδεινώσουν την κατάσταση.

Επιλεκτική χρήση τεράστιων σελίδων

Αφού διαβάσετε αυτό το άρθρο, έχετε καθορίσει ποια μέρη του προγράμματός σας μπορούν να ωφεληθούν από τη χρήση τεράστιων σελίδων και ποια όχι. Πρέπει λοιπόν να ενεργοποιηθούν καθόλου οι τεράστιες σελίδες;

Ευτυχώς μπορείτε να χρησιμοποιήσετε madvise()για να ενεργοποιήσετε την τεράστια σελίδα μόνο για εκείνες τις περιοχές μνήμης όπου θα ήταν χρήσιμο.

Πρώτα, ελέγξτε ότι το largepages εκτελείται σε λειτουργία madvise() χρησιμοποιώντας οδηγίες στην αρχή του άρθρου.

Στη συνέχεια, χρησιμοποιήστε madvise()για να πει στον πυρήνα πού ακριβώς να χρησιμοποιεί τις τεράστιες σελίδες.

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

Σημειώστε ότι αυτή η μέθοδος είναι απλώς μια συμβουλή στον πυρήνα για το πώς να διαχειρίζεται τη μνήμη. Αυτό δεν σημαίνει ότι ο πυρήνας θα χρησιμοποιεί αυτόματα τεράστιες σελίδες για μια δεδομένη μνήμη.

Ανατρέξτε στην τεκμηρίωση (manpage)madviseγια να μάθετε περισσότερα σχετικά με τη διαχείριση μνήμης και madvise(), αυτό το θέμα έχει μια απίστευτα απότομη καμπύλη εκμάθησης. Επομένως, εάν σκοπεύετε να γίνετε πραγματικά καλοί σε αυτό, προετοιμαστείτε να διαβάσετε και να δοκιμάσετε για μερικές εβδομάδες προτού περιμένετε θετικά αποτελέσματα.

Τι να διαβάσω;

Εχω μια ερώτηση? Γράψτε στα σχόλια!

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο