
Преводът на статията е подготвен за студентите от курса .
По-рано споделих как да проверите и активирате използването на Hugepages в Linux.
Тази статия ще бъде полезна само ако наистина имате място за използване на Hugepages. Срещал съм много хора, които са заблудени от перспективата, че Hugepages магически ще подобрят производителността. Все пак, hugepaging е сложна тема и при неправилна употреба може да намали производителността.
Част 1: Проверка дали hugepages са активирани в Linux (оригинал) )
Проблемът:
Необходимо е да проверите дали HugePages са активирани във вашата система.
Решение:
Това е доста просто:
cat /sys/kernel/mm/transparent_hugepage/enabledЩе получите нещо такова:
always [madvise] neverЩе видите списък с налични опции (always, madvise, never), като текущо активната опция ще бъде заключена в скобки (по подразбиране madvise).
madvise означава, че transparent hugepages са активирани само за области на паметта, които явно искат hugepages с помощта на .
always означава, че transparent hugepages са активирани винаги и за всички процеси. Обикновено това повишава производителността, но ако имате вариант на употреба, при който множество процеси консумират малко количество памет, то общото натоварване на паметта може да се увеличи рязко.
never означава, че 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: Променете системната стойност по подразбиране, прекомпилирайки ядрото с изменена конфигурация (този вариант се препоръчва само ако използвате собствено ядро):
- За да настроите always по подразбиране, използвайте:
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y # Закоментирайте CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y - За да настроите madvise по подразбиране, използвайте:
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y # Закоментирайте CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
Част 2: Предимства и недостатъци на HugePages
Ние ще се опитаме изборно да обясним предимствата, недостатъците и възможните грешки при използването на Hugepages. Понеже става дума за технологично сложна и детайлна статия, вероятно тя ще бъде трудна за разбиране за хора, които са заблудени да смятат, че Hugepages са панацея, аз ще жертвам точността в името на простотата. Просто е важно да се запомни, че много теми наистина са сложни и затова са силно опростени.
Обърнете внимание, че говорим за 64-битови x86 системи, работещи на Linux, и че просто предполагам, че системата поддържа transparent hugepages (тъй като не е недостатък, че hugepages не се подменят), каквато е практиката в почти всяка съвременна среда Linux.
В линковете по-долу ще прикрепя повече техническо описание.
Виртуална памет
Ако сте C++ програмист, знаете, че обектите в паметта имат конкретни адреси (значения на указатели).
Въпреки това, тези адреси не задължително отразяват физическите адреси в паметта (адреси в RAM). Те представляват адреси в виртуалната памет. Процесорът има специален модул MMU (memory management unit), който помага на ядрото да съпоставя виртуалната памет с физическото местоположение.
Такъв подход има множество предимства, но най-основните от тях са:
- Производителност (по различни причини);
- Изолация на програмите, тоест нито една от програмите не може да чете от паметта на друга програма.
Какво представляват страниците?
Виртуалната памет е разделена на страници. Всяка отделна страница указва на определена физическа памет; тя може да указва на област в оперативната памет или на адрес, назначен на физическо устройство, например видеокартата.
Повечето страници, с които работите, указват или на RAM, или са подменени (swap), тоест съхранявани на твърдия диск или SSD. Ядото управлява физическото местоположение на всяка страница. Ако се получи достъп до подменена страница, ядро спира потока, който се опитва да получи достъп до паметта, прочита страницата от твърдия диск/SSD в оперативната памет и след това продължава изпълнението на потока.
Този процес е прозрачен за потока, тоест той не чете задължително директно от твърдия диск/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 са страхотни. Те могат да повишат производителността без значителни усилия. Но тук има важни уговорки.
Подмяна на Hugepages.
Ядро автоматично проследява честотата на използване на всяка страница в паметта. Ако физическата памет (RAM) е недостатъчна, ядрото ще премести по-малко важни (по-рядко използвани) страници на твърдия диск, за да освободи част от RAM за по-важни страници.
По принцип, същото важи и за Hugepages. Въпреки това, ядрото може да заменя само цели страници, а не отделни байтове.
Да предположим, че имаме такава програма:
char* mymemory = malloc(2*1024*1024); // Нека приемем това за един Hugepage!
// Попълваме mymemory с някакви данни
// Правим много други неща,
// които ще доведат до подмяна на страницата mymemory
// ...
// Изискваме достъп само до първия байт
putchar(mymemory[0]); В този случай ядрото ще трябва да подмени (прочете) цели 2 мегабайта информация от твърдия диск/SSD, само за да прочетете един байт. Що се отнася до обикновените страници, от твърдия диск/SSD трябва да се прочетат само 4096 байта.
Затова, ако hugepage се подменя, нейното четене става по-бързо, само ако трябва да получите достъп до цялата страница. Това означава, че ако се опитвате да получите случайно достъп до различни части от паметта и просто четете няколко килобайта, трябва да използвате обикновени страници и да не се тревожите повече за това.
От друга страна, ако ви е необходимо последователно да получавате достъп до голяма част от паметта, hugepages ще увеличат вашата производителност. Въпреки това, трябва да проверите това сами (а не на примера на абстрактен софтуер) и да видите какво ще работи по-бързо.
Алоцировка в паметта
Ако пишете на C, знаете, че можете да поискате каквото и да е малко (или почти колкото е възможно голямо) количество памет от купчината с помощта на malloc(). Да предположим, че ви трябват 30 байта памет:
char* mymemory = malloc(30);На програмиста може да му се струва, че вие “изисквате” 30 байта памет от операционната система и получавате указател към някаква виртуална памет. Но всъщност malloc () — това е просто функция на C, която извиква от вътре функциите за искане или освобождаване на памет от операционната система.
Въпреки това, искането на все повече и повече памет за всяка алокация не е ефективно; най-вероятно някакъв сегмент памет вече е бил освободен (free()), и можем да го използваме повторно. malloc() реализира доста сложни алгоритми за повторно използване на освободената памет.
При това всичко става незабелязано за вас, така че защо това трябва да ви притеснява? Ами защото извикването free() не означава, че .
Съществува такова понятие, като фрагментация на паметта. В крайни случаи има сегменти на купчината, в които се използват само няколко байта, докато всичко, което се намира между тях, е било освободено. (free()).
Обърнете внимание, че фрагментацията на паметта е невероятно сложна тема и дори незначителни промени в програмата могат да я повлияят значително. В повечето случаи програмите не предизвикват значителна фрагментация на паметта, но трябва да имате предвид, че ако възникне проблем с фрагментацията в определена област на купчината, hugepages могат само да влошат ситуацията.
Изборно прилагане на hugepages.
След прочитане на статията, вие определихте кои части от вашата програма могат да се възползват от прилагането на hugepages и кои – не. Трябва ли изобщо да включите hugepages?
За щастие, можете да използвате madvise(), за да включите hugepaging само за онези области на паметта, където това ще бъде полезно.
Първо, проверете дали hugepages работят в режим madvise(), с помощта на в началото на статията.
След това, използвайте 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)Имайте предвид, че този метод - това са само препоръки за ядрото относно управлението на паметта. Това не означава, че ядрото автоматично ще използва hugepages за зададената памет.
Консултирайте се с документацията , за да научите повече за управлението на паметта и madvise(), по темата има изключително стръмна крива на обучение. Затова, ако имате намерение наистина да се запознаете с нея, пригответе се за четене и тестване в продължение на няколко седмици, преди да очаквате какъвто и да е положителен резултат.
Какво да прочетете?
Имате въпрос? Напишете в коментарите!
Източник: habr.com
