Перавагі і недахопы 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 уключаны толькі для абласцей памяці, якія відавочна запытваюць 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: Зменіце сістэмнае значэнне па змаўчанні, перакампіляваўшы ядро ​​з змененай канфігурацыяй (гэты варыянт рэкамендуецца толькі калі вы выкарыстоўваеце ўласнае ядро):

  • Каб паставіць always па змаўчанні, выкарыстоўвайце:
    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, і што я проста мяркую, што сістэма падтрымлівае transparent hugepages (бо не з'яўляецца недахопам тое, што hugepages не падмяняюцца), як гэта здараецца практычна ў любой сучаснай асяроддзі Linux.

У спасылках ніжэй я прымацоўваю больш тэхнічнага апісання.

віртуальная памяць

Калі вы праграміст C++, вы ведаеце, што ў аб'ектаў у памяці ёсць пэўныя адрасы (значэнні паказальніка).

Аднак гэтыя адрасы неабавязкова адлюстроўваюць фізічныя адрасы ў памяці (адрасы ў АЗП). Яны ўяўляюць сабой адрасы ў віртуальнай памяці. Працэсар мае спецыяльны модуль MMU (memory management unit), які дапамагае ядру супастаўляць віртуальную памяць з фізічным месцазнаходжаннем.

Такі падыход мае мноства пераваг, але самыя асноўныя з іх:

  • Прадукцыйнасць (па розных прычынах);
  • Ізаляцыя праграм, гэта значыць ні адна з праграм не можа чытаць з памяці іншай праграмы.

Што такое старонкі?

Віртуальная памяць падзелена на старонкі. Кожная асобная старонка паказвае на вызначаную фізічную памяць, яна можа паказваць на вобласць у аператыўнай памяці, а можа на адрас, прызначаны фізічнай прыладзе, напрыклад відэакарце.

Большасць старонак, з якімі вы маеце справу, паказваюць або на АЗП, або падмяняюцца (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

Ядро аўтаматычна адсочвае частату выкарыстання кожнай старонкі памяці. Калі фізічнай памяці (АЗП) недастаткова, ядро ​​перамесціць менш важныя (радзей выкарыстоўваныя) старонкі на цвёрдую кружэлку, каб вызваліць частку АЗП для важнейшых старонак.
У прынцыпе, тое ж самае тычыцца і Hugepages. Аднак ядро ​​можа мяняць месцамі толькі цэлыя старонкі, а не асобныя байты.

Дапусцім, у нас ёсць такая праграма:

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

У гэтым выпадку ядру трэба будзе падмяніць (прачытаць) цэлых 2 мегабайта інфармацыі з цвёрдай кружэлкі/SSD толькі для таго каб вы прачыталі адзін байт. Што да звычайных старонак, з цвёрдай кружэлкі/SSD трэба прачытаць усяго 4096 байт.

Таму, калі hugepage падмяняецца, яе чытанне адбываецца хутчэй, толькі калі вам трэба атрымаць доступ да ўсёй старонкі. Гэта значыць, што калі вы спрабуеце атрымаць доступ выпадковым чынам да розных частак памяці і проста счытваеце пару кілабайт, вам варта выкарыстоўваць звычайныя старонкі і больш ні пра што не турбавацца.

З іншага боку, калі вам трэба атрымліваць доступ да вялікай часткі памяці паслядоўна, hugepages павялічаць вашу прадукцыйнасць. Тым не менш, вам трэба праверыць гэта самастойна (а не на прыкладзе абстрактнага ПЗ) і паглядзець, што будзе працаваць хутчэй.

Алакацыя ў памяці

Калі вы пішаце на З, вы ведаеце, што вы можаце запытаць калі заўгодна малыя (ці амаль калі заўгодна вялікія) аб'ёмы памяці з кучы з дапамогай malloc(). Дапушчальны, вам трэба 30 байт памяці:

char* mymemory = malloc(30);

Праграмісту можа здацца, што вы "запытваеце" 30 байт памяці з аперацыйнай сістэмы і вяртаеце паказальнік на некаторую віртуальную памяць. Але насамрэч malloc () - гэта проста функцыя C, якая выклікае знутры функцыі brk і sbrk для запыту ці вызвалення памяці з аперацыйнай сістэмы.

Аднак, запытваць больш і больш памяці для кожнай алакацыі неэфектыўна; найболей верагодна, што які-небудзь сегмент памяці ўжо быў вызвалены (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 для зададзенай памяці.

Звярніцеся да дакументацыі (manpage) madvise, каб даведацца больш аб кіраванні памяццю і madvise(), У гэтай тэмы неверагодна крутая крывая навучання. Таму, калі вы маеце намер сапраўды добра разабрацца ў ёй, падрыхтуйцеся да чытання і тэставанню на працягу некалькіх тыдняў, перш чым разлічваць на хоць нейкі дадатны вынік.

Што пачытаць?

Ёсць пытанне? Напішыце ў каментарах!

Крыніца: habr.com

Дадаць каментар