مدیریت حافظه در سیستم عامل مبحث نسبتا پیچیده و گسترده ای است که ما در این نوشته قصد ورود به جزییات آن را نداریم ولی برای پاسخ دادن به سوال مطرح شده (“چرا Huge Page؟”)، به ناچار باید با اصطلاحاتی چون Page، Page Table، Page Table Entry و TLB آشنا باشیم، از اینرو، در ابتدای این متن، بعضی از مباحث مقدماتی مدیریت حافظه را مرور می کنیم.
در بیشتر پلتفرمها، فضای حافظه به قسمتهای هم اندازه که اصطلاحا Page نام دارد، تقسیم می شود اندازه صفحات در حالت پیش فرض، برابر با 4k می باشد.
مثلا اگر میزان فضای RAM اختصاص داده شده به سروری برابر با 1MB باشد، تعداد صفحات آن برابر با 256 خواهد بود و به همین شکل حافظه یک گیگابایتی به 256000 صفحه و حافظه 128GB حدودا به 32 میلیون صفحه خواهد رسید.
نکته: برای تعیین اندازه هر صفحه در محیط لینوکس(خانواده Red Hat)، می توان از دستور زیر استفاده کرد:
[root@myhost ~]# getconf PAGESIZE
4096
معمولا در داکیومنتهای سیستم عامل، در کنار اصطلاح Page از اصطلاح Frame هم استفاده می شود با این تعریف که Page بخشی از فضای Virtual memory و Frame هم بخشی از فضای RAM یا همان physical memory می باشد و قرار است اطلاعات در قالب Page در Frame قرار بگیرد به این دلیل، اندازه Page و Frame باید یکسان باشد.
زمانی که پروسسی قصد استفاده از فضای RAM را دارد، CPU فریمی را از حافظه به آن پروسس اختصاص داده و اطلاعات در قالب Page از دیسک به RAM منتقل و در آن فریم(Frame) قرار می گیرند.
این اتفاق در قسمتی از حافظه بنام Page Table ثبت خواهد شد(کدام Page در کدام Frame قرار گرفته است).
با افزایش تعداد پروسسهای سیستم و همچنین بارکاری که پروسسها ایجاد می کنند، حجم page Table متعلق به پروسس و سیستم افزایش پیدا خواهد کرد(در انتهای متن، به صورتی عملی این مسئله را شرح خواهیم داد).
با ادبیات سیستم عاملی باید گفت که قرار است CPU با کمک Page Table، از Logical/Virtual Address به Physical Address برسد(به این عمل، Translation هم گفته می شود)، با این ساختار، هر پروسس برای هر دسترسی به دیتا،باید دو بار به حافظه رجوع کند یکبار برای دسترسی به Page Table و بار دیگر برای دسترسی به خود دیتا.
برای سرعت بخشیدن به این مسئله، در کنار CPU، حافظه نهانی(memory cache) بنام Translation Lookaside Buffer (TLB) قرار گرفته که آخرین page table entryهای مورد رجوع را در خود نگه می دارد با این استدلال که شاید این page table entryها مجددا مورد استفاده قرار بگیرند.
TLB ظرفیت بسیار محدودی دارد و در صورتی که CPU در آن page table entry مورد نظر را پیدا نکند، اصطلاحا TLB miss رخ خواهد داد در این صورت CPU باید به سراغ Page Table برود.
فلوچارت زیر، با جزییات بیشتری موقعیت TLB و Page Table را نمایش می دهد:
در صورتی که اندازه RAM اختصاص داده شده به سرور، در حدی زیاد باشد که تعداد صفحات آن به میلیون و یا بیشتر برسد، TLB Miss بیشتری رخ خواهد داد و به دلیل رشد حجم Page Table، فضای بیشتری از حافظه از دست خواهد رفت و علاوه بر آن، سرعت جستجو هم کاهش پیدا خواهد کرد.
نکته:حجم مصرفی PageTable را می توان با کمک دستور زیر تعیین کرد(البته این مقدار در سیستم عملیاتی، با کم و زیاد شدن پروسسها، دائما در حال تغییر است):
[root@myhost ~]# cat /proc/meminfo | grep PageTables
PageTables: 1102548 kB ==>1GB
با توجه به موضوعات مطرح شده، سیسم عاملها برای آنکه مانع از رشد بی رویه حجم Page Table و همچنین TLB missهای مکرر شوند، به فکر افزایش سایز صفحات افتادند و متعاقب آن؛ مفهوم Huge Page در لینوکس، Large Page در HP-UX و ویندوز و به همین شکل Super Page در FreeBSD مطرح شد.
در صورت پیکربندی Huge Page، اندازه هر Page به صورت پیش فرض به 2MB تنظیم خواهد شد البته امکان تغییر آن تا 1024MB(در صورت پشتیبانی سخت افزاری) هم وجود دارد. همچنین در کنار استفاده از Huge Pgae باز هم می توان بصورت همزمان از Normal Page(صفحات با سایز 4k) هم بهره برد.
در جدول زیر، تفاوت تعداد Pageها را برای فضای حافظه 256 گیگابایتی مشاهده می کنید:
علاوه بر مزیتهای گفته شده، استفاده از Huge Page مزیت دیگری را هم به همراه دارد در صورت استفاده از Huge Page، صفحات در حافظه سنجاق(pin) شده و امکان انتقال این صفحات به بیرون و اصطلاحا swap شدن آنها وجود ندارد. البته کماکان swapping برای صفحاتی که در مخزن Huge Page قرار ندارند(صفحات 4k)، ممکن است رخ دهد.
نکته: در محیط دیتابیس اوراکل؛ برای pin کردن صفحات 4K مربوط به SGA در RAM(جلوگیری از swap کردن این صفحات)، می توان از پارامتر LOCK_SGA=TRUE استفاده کرد.
به صورت خلاصه، استفاده از Huge Page می تواند فواید زیر را به همراه داشته باشد:
1.حجم Page Table را کاهش داده و متعاقب آن، فضای کمتری از RAM از دست خواهد رفت.
2.تعداد TLB missها کمتر می شود در نتیجه CPU راندمان بهتری نسبت به گذشته خواهد داشت.
3.امکان swap کردن این نوع از صفحات(standard Huge Page) وجود ندارد.
توجه: علاوه بر standard Huge Page نوع دیگری از Huge Page هم با عنوان Transparent huge page وجود دارد که درمطلب دیگری در همین سایت، مختصرا به تفاوتهای این دو می پردازیم.
تفاوت حجم Page Table برای normal Page و Huge Page
در این قسمت با کمک اسکریپتی، از طریق ابزار sqlplus به دیتابیس(اوراکل) متصل شده و 2000 کانکشن جدید ایجاد می کنیم. این کار را در دو محیط با صفحات سایز 4KB و 2MB انجام خواهیم داد تا حجم Page Table را با هم مقایسه کنیم(ایده این مقایسه از بلاگ Kevin Closson گرفته شده است.)
***متن اسکریپت
[oracle@myhost ~]$ vi PageTableSzie.sh
cnt=0
until [ $cnt -eq 2000]
do
sqlplus usef/a @sleep.sql &
(( cnt = $cnt + 1 ))
done
wait
[oracle@myhost ~]$ vi sleep.sql
HOST sleep 120
exit;
قرار است اسکریپت را در محیطی با مشخصات زیر اجرا کنیم:
RAM :126GB
sga_max_size=64G
sga_target=64G
اجرای اسکریپت با Page Size=4k:
قبل از اجرای اسکریپت، دیتابیس را استارت کرده و حجم فعلی PAGE TABLE و تعداد پروسسهای دیتابیس اوراکل را با دستورات زیر مشخص می کنیم:
-Huge Page در این محیط پیکربندی نشده:
[root@myhost ~]# cat /proc/meminfo|grep ^Huge
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
-حجم Page Table قبل از استارت دیتابیس:
[root@myhost ~]# grep PageTables /proc/meminfo
PageTables: 17816 kB ==> 17MB
-استارت دیتابیس اوراکل:
SQL> startup
-حجم Page Table بعد از استارت دیتابیس:
[oracle@myhost ~]$ grep PageTables /proc/meminfo
PageTables: 262908 kB ==> 262MB
-تعداد کل پروسسهای دیتابیس اوراکل:
[oracle@myhost ~]$ ps -ef | grep db18c | wc -l
210
در این محیط اسکریپت PageTableSzie.sh را اجرا می کنیم:
[oracle@myhost ~]$ ./PageTableSzie.sh
SQL*Plus: Release 18.0.0.0.0 – Production on Mon Apr 6 15:35:25 2020
Version 18.3.0.0.0
Copyright (c) 1982, 2018, Oracle. All rights reserved.
….
در زمان اجرای این اسکریپت، میزان رشد حجم Page Table و تعداد پروسسهای اوراکلی را مانیتور می کنیم:
[root@myhost ~]# ps -eaf|grep db18c|wc -l;grep PageTables /proc/meminfo
2083==> تعداد کانکشنها به دیتابیس
PageTables: 2717320 kB ==> 2700MB
[root@myhost ~]# ps -eaf|grep db18c|wc -l;grep PageTables /proc/meminfo
2207
PageTables: 4179884 kB ==> 4GB
[root@myhost ~]# ps -eaf|grep db18c|wc -l;grep PageTables /proc/meminfo
2209
PageTables: 4180892 kB ==> 4GB
همانطور که می بینید، حجم Page Table با 2200 پروسس، به 4GB هم رسیده است.
اجرای اسکریپت با Page Size=2MB:
-Huge Page در این محیط پیکربندی شده است:
[root@myhost ~]# grep ^Huge /proc/meminfo
HugePages_Total: 33768
HugePages_Free: 33768
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
-اندازه Page Table “قبل و بعد” از استارت دیتابیس:
[root@myhost ~]# grep PageTables /proc/meminfo
PageTables: 16564 kB ==>16MB
SQL> startup
[root@myhost ~]# grep PageTables /proc/meminfo
PageTables: 123484 kB ==>123MB
اجرای اسکریپت:
[oracle@myhost ~]$ ./PageTableSzie.sh
SQL*Plus: Release 18.0.0.0.0 – Production on Mon Apr 6 17:31:44 2020
Version 18.3.0.0.0
Copyright (c) 1982, 2018, Oracle. All rights reserved.
در زمان اجرای این اسکریپت، میزان رشد حجم Page Table و تعداد پروسسهای اوراکلی را مانیتور می کنیم:
[root@myhost ~]# ps -eaf|grep db18c|wc -l;grep PageTables /proc/meminfo
1823
PageTables: 1410940 kB==>1.5GB
[root@myhost ~]# ps -eaf|grep db18c|wc -l;grep PageTables /proc/meminfo
2208
PageTables: 1658896 kB==>1.6GB
[root@myhost ~]# ps -eaf|grep db18c|wc -l;grep PageTables /proc/meminfo
2210
PageTables: 1658740 kB==>1.6GB
همانطور که می بینید، حجم Page Table با 2200 پروسس، به 1.6GB رسیده است.
تغییر مقدار پیش فرض Huge Page در OL7.5
قبل از پیکربندی Huge Page، باید بررسی شود که CPU تا چه سایزی از Huge Page را پشتیبانی می کند. بطور مثال، عبارت pse در خروجی دستور زیر، به نشانه پشتیبانی CPU از Huge Page با سایز 2MB خواهد بود:
[root@myhost ~]# cat /proc/cpuinfo | egrep -o pse | head -n 1
pse
برای تغییر مقدار پیش فرض Huge Page از 2MB به 1024MB، ابتدا باید اطمینان حاصل کنیم که آیا CPU از Huge Page با سایز یک گیگابایت پشتیانی می کند یا نه؟ برای بررسی این مسئله، دستور زیر را اجرا می کنیم :
[root@myhost ~]# cat /proc/cpuinfo | grep pdpe1gb | head -n 1
pdpe1gb
عبارت pdpe1gb در خروجی این دستور، به معنای پشتیبانی CPU از Huge Page با سایز یک گیگابایت خواهد بود.
برای تغییر مقدار پیش فرض Huge Page از 2MB به 1024MB می توان عبارت default_hugepagesz=1G را به انتهای پارامتر GRUB_CMDLINE_LINUX در فایل etc/sysconfig/grub/ اضافه کرد:
[root@myhost ~]# grep default_hugepagesz /etc/sysconfig/grub
GRUB_CMDLINE_LINUX=”crashkernel=128M rd.lvm.lv=ol/root rd.lvm.lv=ol/swap rhgb quiet numa=off transparent_hugepage=never default_hugepagesz=1G“
برای اعمال نهایی این تغییر، باید grub را بازسازی کنیم:
[root@myhost ~]# grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
Generating grub configuration file …
Found linux image: /boot/vmlinuz-4.1.12-94.3.9.el7uek.x86_64
Found initrd image: /boot/initramfs-4.1.12-94.3.9.el7uek.x86_64.img
Found linux image: /boot/vmlinuz-3.10.0-693.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-693.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-d3f957112e484e61890c1e849a44b850
Found initrd image: /boot/initramfs-0-rescue-d3f957112e484e61890c1e849a44b850.img
Done
با reboot کردن سرور دوباره مقدار Hugepagesize را بررسی می کنیم:
[root@myhost ~]# reboot
[root@myhost ~]# grep Hugepagesize: /proc/meminfo
Hugepagesize: 1048576 kB
همانطور که می بینید، مقدار پیش فرض Hugepagesize از 2MB به 1GB تغییر کرده است.
بسیار مفید بود
خیلی عالی مهندس -خیلی مفید بود
بسیار عالی و کاربردی