Предимства и недостатъци на HugePages

Предимства и недостатъци на HugePages

Превод на статията, подготвен за студентите от курса „Linux администратор“.

По-рано говорих за това как да тествам и активирам Hugepages на Linux.
Тази статия ще бъде полезна само ако наистина имате къде да използвате Hugepages. Срещал съм много хора, които са заблудени от перспективата, че Hugepages магически ще подобри производителността. Въпреки това, hugepaging е сложна тема и може да влоши производителността, ако се използва неправилно.

Част 1: Проверка дали hugepages са активирани на Linux (оригинал тук)

Проблем:
Трябва да проверите дали HugePages е активиран във вашата система.

решение:
Доста е просто:

cat /sys/kernel/mm/transparent_hugepage/enabled

Ще получите нещо подобно:

always [madvise] never

Ще видите списък с налични опции (винаги, madvise, никога), а текущо активната опция ще бъде оградена в скоби (по подразбиране madvise).

madvise означава, че 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-битови x86 системи, работещи с Linux, и че аз просто предполагам, че системата поддържа прозрачни огромни страници (тъй като не е недостатък, че огромните страници не се презаписват), както е в почти всеки съвременен Linux заобикаляща среда.

Ще прикача повече техническо описание във връзките по-долу.

Виртуална памет

Ако сте C++ програмист, знаете, че обектите в паметта имат специфични адреси (стойности на указателя).

Тези адреси обаче не отразяват непременно физическите адреси в паметта (RAM адреси). Те представляват адреси във виртуалната памет. Процесорът има специален модул MMU (memory management unit), който помага на ядрото да картографира виртуалната памет към физическо местоположение.

Този подход има много предимства, но най-важните са:

  • Изпълнение (по различни причини);
  • Програмна изолация, тоест нито една програма не може да чете от паметта на друга програма.

Какво представляват страниците?

Виртуалната памет е разделена на страници. Всяка отделна страница сочи към конкретна физическа памет, може да сочи към област в RAM или може да сочи към адрес, присвоен на физическо устройство, като например видеокарта.

Повечето от страниците, с които работите, или сочат към RAM, или са разменени, което означава, че се съхраняват на вашия твърд диск или SSD. Ядрото управлява физическото оформление на всяка страница. Ако има достъп до подправена страница, ядрото спира нишката, която се опитва да получи достъп до паметта, чете страницата от твърдия диск/SSD в RAM и след това продължава да изпълнява нишката.

Този процес е прозрачен за поток, което означава, че не е задължително да чете директно от HDD/SSD. Размерът на нормалните страници е 4096 байта. Размерът на Hugepages е 2 мегабайта.

Преводно-асоциативен буфер (TLB)

Когато една програма има достъп до страница от паметта, процесорът трябва да знае от коя физическа страница да чете данни (тоест да има виртуална адресна карта).

Ядрото има структура от данни (таблица на страници), която съдържа цялата информация за използваните страници. Използвайки тази структура от данни, можете да нанесете виртуален адрес на физически адрес.

Таблицата на страниците обаче е доста сложна и бавна, така че просто не можем да анализираме цялата структура на данните всеки път, когато даден процес има достъп до паметта.

За щастие нашият процесор има TLB, който кешира картографирането между виртуални и физически адреси. Това означава, че въпреки че трябва да анализираме таблицата на страниците при първия опит за достъп, всички последващи достъпи до страницата могат да бъдат обработени в TLB, което позволява бърза работа.

Тъй като е внедрен като физическо устройство (което го прави бърз на първо място), капацитетът му е ограничен. Така че, ако искате да имате достъп до повече страници, TLB няма да може да съхранява съпоставяния за всички тях, което кара програмата ви да работи много по-бавно.

Hugepages идва на помощ

И така, какво можем да направим, за да избегнем препълването на TLB? (Предполагаме, че програмата все още се нуждае от същото количество памет).

Тук се намесва Hugepages. Вместо 4096 байта, изискващи само един TLB запис, един TLB запис вече може да сочи към огромните 2 мегабайта. Да приемем, че TLB има 512 записа, тук без Hugepages можем да съпоставим:

4096 b⋅512=2 MB

Тогава как можем да се сравним с тях:

2 MB⋅512=1 GB

Ето защо Hugepages е страхотен. Те могат да подобрят производителността без много усилия. Но тук има съществени уговорки.

Спуфинг на огромни страници

Ядрото автоматично следи колко често се използва всяка страница от паметта. Ако няма достатъчно физическа памет (RAM), ядрото ще премести по-малко важни (по-рядко използвани) страници на твърдия диск, за да освободи малко RAM за по-важни страници.
По принцип същото важи и за Hugepages. Въпреки това, ядрото може да разменя само цели страници, а не отделни байтове.

Да кажем, че имаме програма като тази:

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

В този случай ядрото ще трябва да замени (прочете) до 2 мегабайта информация от твърдия диск/SSD, само за да можете да прочетете един байт. Що се отнася до обикновените страници, само 4096 байта трябва да бъдат прочетени от твърдия диск/SSD.

Следователно, ако hugepage е отменена, четенето е по-бързо само ако трябва да получите достъп до цялата страница. Това означава, че ако се опитвате да получите произволен достъп до различни части от паметта и просто четете няколко килобайта, трябва да използвате обикновени страници и да не се притеснявате за нищо друго.

От друга страна, ако имате нужда от достъп до голяма част от паметта последователно, hugepages ще подобри производителността ви. Трябва обаче да го тествате сами (не с абстрактен софтуер) и да видите какво работи по-бързо.

Разпределение в паметта

Ако пишете C, знаете, че можете да поискате произволно малки (или почти произволно големи) количества памет от купчината, като използвате malloc(). Да приемем, че имате нужда от 30 байта памет:

char* mymemory = malloc(30);

За програмист може да изглежда, че „искате“ 30 байта памет от операционната система и връщате указател към някаква виртуална памет. Но всъщност malloc () е просто C функция, която извиква от самата функция брк и сбрк за да поискате или освободите памет от операционната система.

Въпреки това изискването на все повече и повече памет за всяко разпределение е неефективно; най-вероятно някой сегмент от паметта вече е освободен (free())и можем да го използваме повторно. malloc() прилага доста сложни алгоритми за повторно използване на освободената памет.

В същото време всичко се случва незабелязано за вас, така че защо трябва да ви тревожи? Но тъй като предизвикателството free() не означава това паметта задължително се връща веднага в операционната система.

Има такова нещо като фрагментация на паметта. В екстремни случаи има сегменти на купчина, където се използват само няколко байта, докато всичко между тях е освободено (free()).

Моля, имайте предвид, че фрагментирането на паметта е невероятно сложна тема и дори незначителни промени в програмата могат да имат значително въздействие. В повечето случаи програмите няма да причинят значително фрагментиране на паметта, но трябва да сте наясно, че ако има проблем с фрагментирането в някоя област на купчината, огромните страници могат да влошат ситуацията.

Селективно използване на огромни страници

След като прочетете тази статия, вие сте определили кои части от вашата програма могат да се възползват от използването на hugepages и кои не. И така, трябва ли изобщо да се активират hugepages?

За щастие можете да използвате madvise()да разрешите hugepaging само за тези области на паметта, където би било полезно.

Първо проверете дали hugepages работи в режим madvise(), използвайки Directions в началото на статията.

След това използвайте madvise()за да каже на ядрото точно къде да използва 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)

Имайте предвид, че този метод е просто съвет към ядрото как да управлява паметта. Това не означава, че ядрото автоматично ще използва огромни страници за дадена памет.

Обърнете се към документацията (manpage)madviseза да научите повече за управлението на паметта и madvise(), тази тема има невероятно стръмна крива на обучение. Така че, ако възнамерявате да станете наистина добри в това, подгответе се да прочетете и тествате няколко седмици, преди да очаквате положителни резултати.

Какво да чета?

Имам въпрос? Пишете в коментарите!

Източник: www.habr.com

Добавяне на нов коментар