
תרגום המאמר שהוכן לתלמידי הקורס .
בעבר דיברתי על איך לבדוק ולהפעיל את השימוש ב-Hugepages ב- Linux.
מאמר זה יהיה שימושי רק אם יש לך מקום להשתמש בו ב-Hugepages. פגשתי הרבה אנשים שהסיכוי ש-Hugepages ישפר את הפרודוקטיביות בצורה קסומה. עם זאת, hugepaging הוא נושא מורכב ועלול לפגוע בביצועים אם נעשה בו שימוש לא נכון.
חלק 1: בדוק ש-hugepages מופעלים Linux (מְקוֹרִי )
בעיה:
עליך לבדוק אם HugePages מופעל במערכת שלך.
פתרון:
זה די פשוט:
cat /sys/kernel/mm/transparent_hugepage/enabledתקבל משהו כזה:
always [madvise] neverתראה רשימה של אפשרויות זמינות (תמיד, מטורף, אף פעם), והאפשרות הפעילה כעת תהיה מוקפת בסוגריים (כברירת מחדל madvise).
madvise אומר ש transparent hugepages מופעל רק עבור אזורי זיכרון המבקשים במפורש דפי ענק באמצעות .
תמיד אומר ש 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 הוא תרופת פלא, אוותר על הדיוק למען הפשטות. רק כדאי לזכור שהרבה מהנושאים הם באמת מורכבים ולכן מפושטים מאוד.
שימו לב שאנחנו מדברים על מערכות x86 של 64 סיביות הפועלות על Linux, ושאני פשוט מניח שהמערכת תומכת בדפי ענק שקופים (מכיוון שזה לא חיסרון שלא מחליפים דפי ענק), כפי שקורה כמעט בכל סביבה מודרנית. Linux.
אצרף תיאור טכני נוסף בקישורים למטה.
זיכרון וירטואלי
אם אתה מתכנת C++, אתה יודע שלאובייקטים בזיכרון יש כתובות ספציפיות (ערכי מצביע).
עם זאת, כתובות אלו אינן משקפות בהכרח כתובות פיזיות בזיכרון (כתובות RAM). הם מייצגים כתובות בזיכרון וירטואלי. למעבד מודול MMU (יחידת ניהול זיכרון) מיוחד המסייע לליבה למפות זיכרון וירטואלי למיקום פיזי.
לגישה זו יתרונות רבים, אך החשובים שבהם הם:
- ביצועים (מסיבות שונות);
- בידוד תוכנית, כלומר, אף תוכנית לא יכולה לקרוא מהזיכרון של תוכנית אחרת.
מה זה דפים?
זיכרון וירטואלי מחולק לדפים. כל עמוד בודד מצביע על זיכרון פיזי ספציפי, הוא יכול להצביע על אזור ב-RAM, או שהוא יכול להצביע על כתובת שהוקצתה למכשיר פיזי, כגון כרטיס מסך.
רוב הדפים שאתה עוסק בהם מצביעים על זיכרון RAM או מוחלפים, כלומר הם מאוחסנים בכונן הקשיח או ב-SSD שלך. הקרנל מנהל את הפריסה הפיזית של כל עמוד. אם ניגשים לדף מזויף, הליבה עוצרת את השרשור שמנסה לגשת לזיכרון, קורא את העמוד מהכונן הקשיח/SSD לתוך ה-RAM, ולאחר מכן ממשיך בביצוע השרשור.
תהליך זה הוא שקוף בזרם, כלומר אינו קורא בהכרח ישירות מה- HDD/SSD. הגודל של דפים רגילים הוא 4096 בתים. גודל דפי ענק הוא 2 מגה בייט.
מאגר אסוציאטיבי לתרגום (TLB)
כאשר תוכנית ניגשת לדף זיכרון, ה-CPU חייב לדעת מאיזה עמוד פיזי לקרוא נתונים (כלומר, יש לה מפת כתובת וירטואלית).
לקרנל יש מבנה נתונים (טבלת דפים) שמכיל את כל המידע על הדפים שבהם נעשה שימוש. באמצעות מבנה נתונים זה, ניתן למפות כתובת וירטואלית לכתובת פיזית.
עם זאת, טבלת הדפים מורכבת ואיטית למדי, כך שפשוט איננו יכולים לנתח את כל מבנה הנתונים בכל פעם שתהליך ניגש לזיכרון.
למרבה המזל, למעבד שלנו יש 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.
לכן, אם עוקף דף ענק, זה מהיר יותר לקריאה רק אם אתה צריך לגשת לכל העמוד. זה אומר שאם אתה מנסה לגשת באופן אקראי לחלקים שונים של הזיכרון ורק קורא כמה קילובייטים, עליך להשתמש בדפים רגילים ולא לדאוג לשום דבר אחר.
מצד שני, אם אתה צריך לגשת לחלק גדול מהזיכרון ברצף, דפי ענק ישפרו את הביצועים שלך. עם זאת, אתה צריך לבדוק את זה בעצמך (לא עם תוכנה מופשטת) ולראות מה עובד מהר יותר.
הקצאה בזיכרון
אם אתה כותב C, אתה יודע שאתה יכול לבקש כמויות זיכרון קטנות באופן שרירותי (או כמעט שרירותי) של זיכרון מהערימה באמצעות malloc(). נניח שאתה צריך 30 בתים של זיכרון:
char* mymemory = malloc(30);למתכנת, אולי נראה שאתה "מבקש" 30 בתים של זיכרון ממערכת ההפעלה ומחזיר מצביע לזיכרון וירטואלי כלשהו. אבל למעשה malloc () היא רק פונקציה C שקוראת מתוך הפונקציה לבקש או לפנות זיכרון ממערכת ההפעלה.
עם זאת, בקשת יותר ויותר זיכרון עבור כל הקצאה אינה יעילה; סביר להניח שקטע זיכרון כלשהו כבר שוחרר (free()), ואנחנו יכולים לעשות בו שימוש חוזר. malloc() מיישמת אלגוריתמים מורכבים למדי לשימוש חוזר בזיכרון משוחרר.
יחד עם זאת, הכל קורה לך בלי לשים לב, אז למה זה צריך להדאיג אותך? אלא בגלל האתגר free() לא אומר את זה .
יש דבר כזה פיצול זיכרון. במקרים קיצוניים, ישנם מקטעי ערמה שבהם נעשה שימוש רק בכמה בתים, בעוד שכל מה שביניהם שוחרר (free()).
שימו לב שפיצול זיכרון הוא נושא מורכב להפליא, ואפילו שינויים קלים בתוכנית יכולים להיות בעלי השפעה משמעותית. ברוב המקרים, תוכניות לא יגרמו לפיצול זיכרון משמעותי, אך עליך להיות מודע לכך שאם יש בעיה עם פיצול באזור כלשהו של הערימה, דפי ענק יכולים להחמיר את המצב.
שימוש סלקטיבי בדפי ענק
לאחר קריאת מאמר זה, החלטת אילו חלקים בתוכנית שלך יכולים להפיק תועלת משימוש ב- Hugepages ואילו לא. אז האם צריך בכלל להפעיל עמודי ענק?
למרבה המזל אתה יכול להשתמש madvise()כדי לאפשר איפיון ענק רק עבור אזורי הזיכרון שבהם זה יהיה שימושי.
ראשית, בדוק ש-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)שימו לב ששיטה זו היא פשוט עצה לקרנל כיצד לנהל זיכרון. זה לא אומר שהקרנל ישתמש אוטומטית בדפי ענק עבור זיכרון נתון.
עיין בתיעוד כדי ללמוד עוד על ניהול זיכרון ו madvise(), לנושא זה יש עקומת למידה תלולה להפליא. אז אם אתה מתכוון להיות ממש טוב בזה, התכונן לקרוא ולבדוק במשך כמה שבועות לפני שאתה מצפה לתוצאות חיוביות.
מה לקרוא?
יש שאלה? כתבו בתגובות!
מקור: www.habr.com
