به سراغ یکی از مهمترین مفاهیم در سیستمهای مدرن یعنی NUMA یا Non-Uniform Memory Access میرویم.
NUMA را میتوان پل ارتباطی میان پردازنده (CPU) و حافظه (Memory) دانست. مفهومی که تأثیر بسیار عمیقی روی عملکرد حافظه دارد، اما تنظیمات و پیکربندی اصلی آن بیشتر در سمت پردازنده انجام میشود.
در محیطهای مجازیسازی، درک درست از معماری NUMA برای بهدست آوردن حداکثر کارایی حیاتی است. هرچند تنظیمات و گزینههای مربوط به NUMA عمدتاً در سطح CPU و هایپروایزر صورت میگیرد، اما اثرات اصلی و قابل توجه آن روی دسترسی به حافظه و در نهایت عملکرد ماشینهای مجازی دیده میشود.
به عبارت سادهتر NUMA مشخص میکند که هر هسته یا گروهی از هستههای پردازنده به کدام بخش از حافظه RAM دسترسی سریعتری دارد و این موضوع میتواند تفاوت چشمگیری در Latency و Bandwidth حافظه ایجاد کند.
مفهوم نوما و یوما(NUMA & UMA)
قبل از پرداختن به NUMA ، بهتر است ابتدا با تکنولوژی قدیمیتر یعنی UMA یا Uniform Memory Access آشنا شویم.
در معماری UMA (که در سیستمهای چندپردازنده قدیمیتر رایج بود)، همه پردازندهها (یا سوکتها) برای دسترسی به تمام حافظه RAM از یک مموریکنترلر مشترک استفاده میکردند. این موضوع باعث ایجاد گلوگاه (Bottleneck) جدی میشد. هرچه تعداد هستهها و سوکتها بیشتر میشد، ترافیک روی آن مموریکنترلر مشترک افزایش پیدا میکرد و در نتیجه Latency بالا و استفاده ناکارآمد از پهنای باند حافظه به وجود میآمد.اما با ظهور معماری NUMA، این مشکل به شکل هوشمندانهای حل شد.
در NUMA ، هر سوکت پردازنده دارای مموریکنترلر اختصاصی خودش است و مستقیماً به اسلاتهای حافظه مربوط به خودش (Local Memory) متصل میشود. به همین دلیل است که در سرورهای فیزیکی همیشه تأکید میشود رمها را بهصورت متعادل و طبق الگوی مشخص در اسلاتهای هر سوکت نصب کنیم تا بالانس حفظ شود.
- حافظهای که مستقیماً به مموریکنترلر همان سوکت وصل است، همان Local Memory می باشد.
- حافظهای که متعلق به سوکت دیگر است، Remote Memory نام دارد.
ترکیب یک سوکت پردازنده + حافظه لوکال متصل به آن را یک NUMA Node یا گاهی NUMA Home Node مینامند.
به زبان ساده هر NUMA Node مثل یک جزیره مستقل ولی متصل عمل میکند تا وقتی پردازنده به حافظه لوکال خودش دسترسی دارد، همه چیز سریع و بهینه است.اما اگر حافظه لوکال تمام شود و مجبور شود از حافظه نود دیگر استفاده کند، عملکرد بهطور قابل توجهی افت میکند.
درک دقیق توپولوژی NUMA ،کلید اصلی بهینهسازی ماشینهای مجازی در محیطهای مجازیسازی است، موضوعی که در بخشهای بعدی بهطور کامل به آن میپردازیم.
نقش NUMA Scheduler در مجازیسازی
هایپروایزرها بهخصوص VMware ESXi دارای یک جزء بسیار هوشمند به نام NUMA Scheduler هستند. وظیفه اصلی این اسکژولر این است که ماشینهای مجازی (VM) را طوری روی نودهای NUMA قرار دهد که تا حد ممکن فقط از Local Memory استفاده کنند و نیازی به دسترسی به Remote Memory پیدا نکنند.
به بیان سادهتر NUMA Scheduler همیشه سعی میکند کل vCPU ها و حافظه یک VM را داخل یک NUMA Node نگه دارد. به این کار NUMA Alignmentیا vNUMA Locality میگویند. وقتی این همترازی رعایت شود، ماشین مجازی تقریباً همان عملکردی را دارد که روی سرور فیزیکی باید میداشت.
البته تنظیماتی که ما بهعنوان ادمین انجام میدهیم میتواند این رفتار هوشمند را مختل کند. مثلاً:
- تخصیص بیش از حد vCPU یا RAM به یک VM بزرگتر از ظرفیت یک NUMA Node
- پین کردن دستی vCPUها به هستههای خاص (CPU Affinity) بدون توجه به توپولوژی NUMA
- غیرفعال کردن قابلیت NUMA در تنظیمات VM
در این موارد، ESXi مجبور میشود VM را روی چند NUMA Node مختلف اسپلیت (split) کند و در نتیجه بخشی از حافظه بهصورت Remote Memory استفاده میشود که افت عملکرد قابل توجهی به همراه دارد.
چند مفهوم کلیدی در مجازیسازی CPU
vCPU چیست؟
- یک vCPU در ESXi معادل یک هسته فیزیکی (Physical Core) یا یک Logical Processor در صورت فعال بودن Hyper-Threading است.
- اما از نظر قدرت، یک vCPU معمولاً قویتر از یک هسته فیزیکی واقعی عمل میکند؛ چون هایپروایزر بهصورت هوشمند زمانبندی میکند و میتواند در هر لحظه از چندین هسته فیزیکی برای اجرای یک vCPU استفاده کند (به این پدیده میگویند overcommitment هوشمند)
- ماشین مجازی به هسته خاصی چسبیده نیست. vCPUها بهصورت داینامیک بین تمام هستههای موجود پخش میشوند، مگر اینکه شما بهصورت دستی CPU Affinity یا CPU Pinning تنظیم کنید (که معمولاً توصیه نمیشود مگر در موارد خیلی خاص).
پخش vCPUها (vCPU Scheduling)
VMKernel و اسکژولر ESXi مسئول پخش عادلانه و هوشمند vCPU ها روی تمام هستههای فیزیکی سرور هستند ، صرفنظر از اینکه سرور چند سوکته باشد یا چند هسته داشته باشد. این پخش کاملاً شفاف و خودکار انجام میشود و هدفش حداکثر کردن بهرهوری و کاهش Latency است.
Hyper-Threading و نگاه NUMA Scheduler به آن
- NUMA Scheduler فقط هستههای واقعی (Physical Cores) را در محاسبات خودش در نظر میگیرد وHyper-Threading را بهعنوان هسته مستقل حساب نمیکند.
- یعنی اگر یک سوکت ۲۰ هسته فیزیکی و ۴۰ Logical Processor با Hyper-Threading داشته باشد
NUMA Node سایز آن ۲۰ در نظر گرفته میشود، نه ۴۰.
- این رفتار بسیار مهم است؛ چون اگر شما به یک VM بیشتر از ۲۰ vCPU بدهید (در این مثال)، حتماً روی
چند NUMA Node اسپلیت میشود، حتی اگر Hyper-Threading فعال باشد.
به همین دلیل همیشه توصیه میشود:
- تعداد vCPU یک ماشین مجازی را کمتر یا مساوی با تعداد Physical Coreهای یک NUMA Node نگه دارید مگر اینکه واقعاً نیاز به VM خیلی بزرگ داشته باشید و افت عملکرد Remote Memory را بپذیرید.
نوما کلاینت و Wide VM
NUMA Client چیست؟
هر ماشین مجازی در ESXi یک (یا چند) NUMA Client دارد NUMA Client .در واقع نمای NUMA از یک VM است و شامل مجموعهvCPU ها و حافظه تخصیصیافته به آن VM میشود که قرار است روی یک NUMA Node اجرا شود.
NUMA Client فقط مخصوص مدیریت CPU و Memory در سطح NUMA است.
ESXi بهصورت خودکار برای هر VM یک یا چند NUMA Client میسازد و سعی میکند هر NUMA Client را کاملاً روی یک NUMA Home Node فیزیکی قرار دهد تا حداکثر عملکرد از Local Memory بهدست آید.
Wide VM چیست؟
وقتی یک ماشین مجازی آنقدر بزرگ باشد که در یک NUMA Node جا نشود، به آن Wide VM میگویند.
Wide VM یعنی:
- تعداد vCPUهای VM بزرگتر از تعداد Physical Core های یک سوکت یا یک NUMA Node باشد.
- مقدار RAM تخصیصیافته به VM بزرگتر از حافظه Local یک نود باشد.
در این حالت ESXi رفتار زیر را انجام میدهد:
- بهجای یک NUMA Client ، چندین NUMA Client (به تعداد نودهای مورد نیاز) برای آن VM میسازد.
- vCPUها و حافظه را بین این NUMA Client ها تقسیم میکند.
- هر NUMA Client به یک NUMA Home Node فیزیکی متصل میشود.
نکته مهم : وقتی VM تبدیل به Wide VM میشود، دیگر نمیتوان کاملاً از Remote Memory جلوگیری کرد. بخشی از دسترسیها بیننودی خواهد بود و افت عملکرد معمولاً ۱۰ تا ۳۰ درصد بسته به workload اجتنابناپذیر است.
به همین دلیل در محیطهای تولید معمولاً سعی میکنند اندازهVM ها را طوری تنظیم کنند که Wide VM نشوند، مگر اینکه واقعاً برنامه نیاز به بیش از یک سوکت کامل داشته باشد مثل دیتابیسهای خیلی بزرگ.
وینوما (vNUMA / Virtual NUMA)
بهطور پیشفرض، سیستمعامل مهمان (Guest OS) داخل یک ماشین مجازی هیچ اطلاعی از توپولوژی NUMA فیزیکی سرور ندارد و همهچیز را بهصورت یک حافظه یکنواخت (UMA) میبیند. این باعث میشود که برنامهها و خود سیستمعامل نتوانند بهینهسازیهای مخصوص NUMA مثل Memory Locality یا Thread Placement را انجام دهند.
برای استفاده از vNUMA دو شرط زیر باید برقرار باشد:
- ماشین مجازی باید حداقل ۹ vCPU داشته باشد.
- ماشین مجازی باید Wide VM باشد؛ یعنی اندازهآن بزرگتر از ظرفیت یک NUMA Node فیزیکی باشد.
تنظیمات بهینه CPU در ماشین مجازی
یکی از مهمترین تنظیماتی که مستقیماً روی عملکرد حافظه و NUMA تأثیر میگذارد، نحوهٔ تنظیم Sockets و Cores per Socket در مشخصات VM است.
چرا این تنظیم مهم است؟
- تنظیم اشتباه باعث میشود توپولوژی vNUMA با توپولوژی NUMA فیزیکی همخوانی نداشته باشد.
- نتیجه: افت عملکرد حافظه، افزایش دسترسی به Remote Memory و حتی رفتار غیرقابل پیشبینی برنامههای NUMA-aware.
قانون طلایی بهینهسازی
همیشه سعی کنید تعداد Cores per Socket در VM دقیقاً برابر با تعداد Physical Cores در هر سوکت سرور باشد (یا مضرب صحیح آن).
به این ترتیب:
- vNUMA دقیقاً همان شکل و اندازه NUMA Node های فیزیکی را به Guest OS نشان میدهد.
- برنامهها بهترین Memory Locality را خواهند داشت.
- NUMA Scheduler کمترین دردسر را خواهد داشت.
نکات مهم و کاربردی در تنظیم Cores per Socket
- از VM تکسوکتی (۱ سوکت + همه کورها) تا حد امکان اجتناب کنید حتی اگر تعداد vCPU کم باشد.
بهتر است Cores per Socket را برابر با تعداد کورهای واقعی هر سوکت فیزیکی تنظیم کنید. این کار باعث میشود vNUMA از همان ابتدا درست expose شود و در آینده اگر VM بزرگتر شد، نیازی به تغییر تنظیمات نباشد.
- محدودیتهای سیستمعامل را فراموش نکنید بسیاری از سیستمعاملها و نسخههای لایسنس روی تعداد سوکت نه تعداد کور یا vCPU کل محدودیت دارند.
بهینهسازی حافظه(Memory) در ماشینهای مجازی
قانون طلایی حافظه در NUMA
درست همانطور که در سرور فیزیکی رمها را بین سوکتها بالانس میکنیم (مثلاً ۱۲۸+۱۲۸ گیگ در سرور دوسوکته)، در مجازیسازی هم باید رم هر VM را طوری تنظیم کنیم که تا حد ممکن فقط از Local Memory یک نود استفاده کند.
تنظیم پیشرفته numa.vcpu.maxPerMachineNode :
این پارامتر پیشرفته تعیین میکند حداکثر چند vCPU میتواند در یک NUMA Client یک Virtual NUMA Node قرار بگیرد. اگر مقدار این پارامتر را کم کنیم، ESXi زودتر از حد معمول VM را به چند NUMA Client تقسیم میکند ،حتی اگر vCPU کم باشد.
جمعبندی NUMA در یک نگاه
در نهایت، تمام تلاش ما در بحث NUMA برای این است که ماشین مجازی تا حد ممکن از Remote Memory استفاده نکند.
چون:
- دسترسی به Remote Memory latency بسیار بالاتری دارد، گاهی ۱.۵ تا ۲ برابر Local Memory.
- هرچه CPU بیشتر منتظر داده بماند، عملاً قدرت پردازشیاش هدر میرود.
- CPU سریعترین قطعه سیستم است، اما همیشه منتظر بقیه است: مموری – شبکه – درایو.
تقسیمبندی تأثیر NUMA بهطور واقعی
- حدود ۶۰–۷۰٪ از بهینهسازیهای NUMA مستقیماً برای بهبود عملکرد حافظه Memory Locality و کاهش Remote Access است.
- ۳۰–۴۰٪ باقیمانده بهطور غیرمستقیم باعث میشود CPU کمتر منتظر بماند و واقعاً بتواند پردازش کند.
اگر این موارد را رعایت کنید، ماشینهای مجازیتان تقریباً همان عملکردی را خواهند داشت که روی سرور فیزیکی بهینهشده انتظار دارید — گاهی حتی بهتر!

