تصور کنید داشبورد نظارتی شما ۴۰٪ ظرفیت خالی CPU را نشان میدهد، اما کاربرانتان از قطع شدن ارتباط و تأخیرهای مرگبار شکایت میکنند. این تضاد، تلهای عملیاتی است که در محیطهای استنتاج اشتراکی رخ میدهد و باعث فروپاشی خاموش عملکرد سیستم میشود. این وضعیت زمانی اتفاق میافتد که ابزارهای بستهبندی (Batch Packers)، زمینههای (Contexts) بیش از حد بزرگ را بدون جداسازی مناسب روی یک ماشین واحد میریزند.
این مشکل زمانی شدت میگیرد که تیمها برای کاهش هزینهها به سراغ استنتاج در میزبانهای «رایگان» یا اشتراکی میروند. در این محیطها، فرآیند مدل زبانی بزرگ (LLM) — مثل کتابخانهداری که میلیاردها صفحه را خوانده و حالا با همان لحن کتابها جواب میدهد — همچنان در حالت runnable (قابل اجرا) باقی میماند، اما سیستم بهجای محاسبه توکنها، تمام وقت خود را صرف بازیابی صفحات حافظه (Memory Pages) میکند. برای یک توسعهدهنده، نمودارها سالم به نظر میرسند، اما کاربر با خطای Timeout مواجه میشود؛ توکنها در نهایت تولید میشوند، اما بسیار دیرتر از بازه زمانی مجاز (Deadline Window) میرسند. در این حالت، هزینه سختافزار رایگان به نظر میرسد، اما تأخیرهای دم (Tail Latency) بسیار گران تمام میشوند.
همانطور که در تحلیلهای قبلی ما دربارهی بهینهسازی هزینههای استنتاج اشاره کردیم، صرفهجویی در سختافزار بدون نظارت دقیق بر لایههای پایینتر سیستم، منجر به کاهش کیفیت تجربه کاربری میشود.
مکانیسم توقف خاموش
به نقل از راهنمای فنی منتشر شده در dev.to در ۱۳ سپتامبر ۲۰۲۶، ریشه این مشکل در شکاف میان معیارهای بیکاری CPU و فشار واقعی حافظه است. وقتی یک فرآیند Worker از حد مجاز حافظه در cgroup فراتر میرود، هسته سیستمعامل (Kernel) شروع به بازیابی صفحات میکند. این عملیات لزوماً باعث جهش مصرف CPU نمیشود، اما پیشرفت Worker را متوقف میکند. در واقع، معیار CPU Idle نمیتواند خروج صفحات ناشناس (Anonymous Pages) از مجموعه کاری (Working Set) را ببیند و معیار سن صف (Queue Age) نیز نمیتواند Worker قابل اجرایی را که منتظر بازیابی حافظه است، شناسایی کند.
برای شناسایی و افشای این وضعیت، این راهنما یک توپولوژی آزمایشگاهی را پیشنهاد میدهد که شامل موارد زیر است:
- یک میزبان اشتراکی: که یک Worker استنتاج واحد را اجرا میکند.
- یک لیست Redis: که به عنوان صف پذیرش و تخلیه (Admission and Drain Queue) عمل میکند.
- یک Sidecar: که وظیفه نمونهبرداری از PSI، Swap و سن صف را بر عهده دارد.
- یک Packer: که قادر است درخواستهایی با پنجره زمینه (Context Window) — شبیه میز کاری که جا برای چند ورق دارد، نه برای کل کتابخانه — بسیار بزرگ ارسال کند.
- محدودیت حافظه cgroup: که کمتر از حجم مجموعه کاری باز شده تنظیم شده است (به عنوان مثال:
memory.max = 2Gوmemory.swap.max = 0).
این توپولوژی به اپراتورها اجازه میدهد یک بار کاری تعریف شده را تست کنند؛ مثلاً خلاصهسازی آفلاین با یک ضربالاجل سخت، جایی که ۸ فایل (حدود ۲۴ هزار توکن متن Prompt) به صورت ۸ درخواست همزمان روی یک فرآیند Worker بستهبندی میشوند. هدف این است که از لحظه قرارگیری در صف تا رسیدن اولین توکن مفید، حداکثر ۸ ثانیه زمان طی شود، در حالی که میزبان دارای ۴ vCPU و محدودیت حافظه ۲ گیگابایت است.
سیگنالهایی که نمودار CPU را شکست میدهند
بر اساس مستندات این راهنما، معیارهای سنتی مانند سن صف و بیکاری CPU شکست میخورند زیرا نمیتوانند خروج صفحات ناشناس از Working Set را رصد کنند. برای حل این مشکل، توصیه میشود فیلدهای تلهمتری زیر هر ثانیه رصد شوند:
- PSI (Pressure Stall Information): بهویژه مقدار
psi_mem_some_avg10از مسیر/proc/pressure/memoryکه درصد زمانی را نشان میدهد که وظایف بهدلیل حافظه متوقف (Stall) شدهاند. در یک تست آزمایشگاهی، این عدد اغلب در عرض ۲۰ ثانیه به بالای ۱۵.۰۰ میرسد، در حالی که CPU Idle همچنان بالای ۳۰٪ باقی میماند. - Swap-in (si): رصد دستور
vmstat 1برای مشاهده صفحات ورودی از Swap در هر ثانیه. اگر مقدارsi > 0باشد در حالی که Swap در cgroup غیرفعال است، نشاندهنده نشت (Leak) در cgroup است. - Deadline Slack: محاسبه
deadline_slack_ms(ضربالاجل منهای زمان فعلی منهای سن صف). وقتی این مقدار پیش از شروع تلاشهای مجدد (Retries) منفی شود، تضاد نمودار و واقعیت اثبات شده است. - تعداد توکنهای بستهبندی شده (Packed Tokens): رصد مجموع توکنهای Prompt در جریان (مثلاً سقف ۳۲ هزار توکن برای یک میزبان ۲ گیگابایتی) بهجای رصد صرف تعداد درخواستها.
- سن صف (Queue Age): استخراج شده از
LINDEXدر Redis بهعلاوه زمان ورود به صف. این معیار برای کارهای منتظر مفید است، اما در طول توقفهای runnable دچار تأخیر میشود.
پیادهسازی دروازه پذیرش (Admission Gate)
به جای پذیرش کورکورانه درخواستها، ابزاری به نام admitd پیشنهاد شده است که نقش دروازهبان را ایفا میکند و درخواستها را بر اساس آستانههای سخت رد میکند. منطق این سیستم از یک اولویت سخت پیروی میکند: رد درخواست اگر psi_mem_some_avg10 از ۱۰.۰۰ فراتر رود، رد درخواست اگر deadline_slack_ms به زیر ۱۵۰۰ میلیثانیه برسد، یا رد درخواست اگر مجموع توکنهای در جریان بهعلاوه توکنهای درخواست جدید از حد مجاز PACKED_TOKEN_LIMIT (مثلاً ۳۲,۰۰۰ توکن) عبور کند.
یک هشدار حیاتی در این راهنما وجود دارد: «تلاش مجدد کورکورانه» (Blind Retries) را متوقف کنید. بازگرداندن یک درخواست رد شده به همان Worker تحت فشار، صرفاً چرخه بازیابی حافظه را تکرار میکند. این کار در واقع زمان واقعی (Wall-clock time) را دو بار میسوزاند بدون اینکه حتی یک توکن تولید شود. تلاشهای مجدد باید به یک کلاس میزبان کاملاً متفاوت هدایت شوند تا از تکرار فرآیند بازیابی حافظه جلوگیری شود.
هزینه بازیابی حافظه
برای اندازهگیری هزینه واقعی یک درخواست بستهبندی شده، باید فراتر از توکنهای ورودی نگریست. این راهنما یک فرمول هزینه جایگزین (Cost Proxy) را برای حسابرسی توکنها، زمان و تلاشهای مجدد معرفی میکند:
cost_proxy = prompt_tokens + completion_tokens + (retry_count * prompt_tokens) + (stall_ms * workers_held / 1000)
این فرمول زمانی را که یک جایگاه Worker در حین توقف حافظه اشغال شده است، محاسبه میکند. مقدار stall_ms_p99 از PSI استخراج میشود و نه از Tokenizer. اگر deadline_slack_ms کمتر از زمان توقف p99 بهعلاوه یک بافر ۵۰۰ میلیثانیهای باشد، آن درخواست یک «شرطبندی بازنده» است و باید فوراً رد شود. این یک دروازه عملیاتی است، نه یک بحث درباره کیفیت مدل.
نردههای حفاظتی عملیاتی
برای جلوگیری از حوادث در محیط عملیاتی، پیشنهاد میشود ابتدا «تزریق خطا» (Fault Injection) بهصورت محلی در localhost انجام شود. با ایجاد یک cgroup محدود و اجرای اسکریپت پایتونی که یک نقشه ناشناس (Anonymous Map) ۳ گیگابایتی را لمس میکند، اپراتورها میتوانند افزایش PSI را قبل از اینکه هسته سیستمعامل Worker را از طریق OOM-kill متوقف کند، مشاهده کنند. این کار تضمین میکند که شکست سیستم «بلند» (مشخص) باشد و دروازه پذیرش پیش از بازه زمانی درخواستهای مشتری تست شود.
در صورت وقوع حادثه در محیط عملیاتی و منفی ماندن Slack، اپراتورها باید مسیر بازگشت (Rollback) زیر را دنبال کنند:
۱. تغییر وضعیت admitd به reject_all با دلیل host_pressure.
۲. توقف Packer (اما توقف ندادن Redis تا زمانی که تخلیه صف کامل شود).
۳. متوقف کردن یا تغییر نام لیست Redis به jobs_drain.
۴. خاتمه دادن به فرآیندهای بستهبندی شده و پاکسازی cgroup (rmdir /sys/fs/cgroup/infer).
۵. انتقال لیست تخلیه (Drain List) به یک کلاس میزبان اختصاصی.
۶. فعالسازی مجدد پذیرش تنها پس از اینکه PSI avg10 زیر ۱.۰۰ باقی ماند.
محدودیتها و دامنه کاربرد
این رویکرد بهطور خاص برای بارهای کاری با ضربالاجل سخت، مانند خلاصهسازی آفلاین، طراحی شده است. فرض بر این است که اپراتور میتواند cgroup memory.max را تنظیم کرده و PSI را در همان میزبان بخواند. این روش برای موارد زیر در نظر گرفته نشده است:
- سرویسهای استنتاج کاملاً مدیریتشده (Managed) که دسترسی به لایه میزبان برای استخراج داده ندارند.
- کارهای دستهای (Batch) که ضربالاجل سخت یا بودجه توکن مشخصی ندارند.
- سیستمهایی که نمیتوانند بدون از دست دادن کارهای ضروری، در حالت بسته (Fail Closed) قرار گیرند.
- طراحیهای اجماع چند منطقهای (Multi-region consensus).
این تغییر در نظارت، فرض بنیادی عملیات هوش مصنوعی (AI Ops) را تغییر میدهد: بیکاری CPU دیگر معیاری برای ظرفیت خالی نیست. در دنیای استنتاج میزبانهای اشتراکی، فشار حافظه تنها معیاری است که واقعاً تعیینکننده توافقنامه سطح خدمات (SLO) است. نمودار سبز CPU در یک میزبان رایگان، یک SLO نیست؛ بلکه شرطبندی روی یک سقوط احتمالی با شرط توقف سخت است.
برای تست این مفاهیم در محیطی امن، توسعهدهندگان میتوانند از سرورهای رایگان MonkeyCode استفاده کنند تا بدون ریسک برای کاربران، تمرینات بستهبندی درخواستها و رصد PSI را انجام دهند. این پلتفرم که پیشتر در شناسایی حذفهای تصادفی تستها در کد کارآمدی خود را ثابت کرده است، اکنون محیطی مناسب برای تحلیلهای زیرساختی فراهم میکند. این کار به تیمها اجازه میدهد دلایل رد درخواستها را اثبات کرده و توانایی Sidecar خود در مشاهده PSI را پیش از ارتقای Worker به محیط عملیاتی، حسابرسی کنند.
گام بعدی شما
- بررسی مقدار
/proc/pressure/memoryدر سرورهای فعلی خود برای شناسایی توقفهای پنهان حافظه. - جایگزینی معیارهای CPU-based با PSI در سیستمهای هشدار (Alerting) برای مدلهای استنتاجی.
- پیادهسازی یک لایه پذیرش (Admission Gate) که بر اساس تعداد توکنهای در جریان و نه تعداد درخواستها، تصمیم میگیرد.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو