پرش به محتوای اصلی
پرش به محتوای مقاله

سنِ صف در برابر ظرفیت GPU؛ تغییر معیار در منطق تکرار درخواست‌ها

·۲۰ شهریور ۱۴۰۵۸ دقیقه مطالعه۱ بازدید
راهنما
رد تلاش‌های نابینا پیش از بیکاری کارگر آزاد قبل از اتمام مهلت زمانی slack
رد تلاش‌های نابینا پیش از بیکاری کارگر آزاد قبل از اتمام مهلت زمانی slack
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

جایگزینی سیگنال «مصرف سخت‌افزار» با «سن صف و مهلت زمانی» برای مدیریت تکرار درخواست‌ها؛ این یک چرخش از نظارت بر ظرفیت به نظارت بر زمان است.

تصور کنید ساعت ۲:۱۴ صبح است و هشدار سیستم استنتاج شما فعال می‌شود؛ داشبورد تماماً سبز است، اما سیستم در حال فروپاشی است. در حالی که مصرف CPU تنها ۱۲٪ است، سن صف به ۴۷ ثانیه رسیده، در حالی که مهلت زمانی (Deadline) شما ۳۰ ثانیه بوده است؛ یعنی هدف سطح خدمات (SLO) شما پیش از این مرده است.

این سناریو که در راهنمای فنی منتشر شده در ۱۱ سپتامبر ۲۰۲۶ توسط dev.to بررسی شده، یک نقص حیاتی در نحوه مدیریت شکست‌های استنتاج (Inference) — لحظه‌ای که مدل واقعاً جواب تولید می‌کند، شبیه به خودِ آشپزی و نه دوره‌ی آموزش آشپز — را افشا می‌کند. بسیاری از سیستم‌ها به‌گونه‌ای پیکربندی شده‌اند که تا زمانی که «فضای خالی» روی سرور وجود دارد، درخواست‌های شکست‌خورده را تکرار کنند. اما ظرفیت خالی نمی‌تواند زمان جدید خلق کند؛ یک کپی رایگان از سرور نمی‌تواند مهلتی را که سپری شده است، بازگرداند. شما ممکن است یک پاسخ را سه بار تکرار کنید، اما ظرفیت خالی فقط حجم کارهای پرداخت‌نشده را زیاد می‌کند. باید تکرار را پیش از آنکه زمان باقی‌مانده به صفر برسد، رد کنید.

توهم پنل‌های سبز

در بسیاری از محیط‌های عملیاتی، اپراتورها مصرف پایین CPU را مجوز بازپخش درخواست‌ها می‌دانند. این یک شرط‌بندی خطرناک است زیرا تکرارها، کارهایی را تکثیر می‌کنند که پیش از این مهلت خود را از دست داده‌اند. وقتی کلاینت کورکورانه درخواستی را که دیر شده تکرار می‌کند، فقط محاسبات را هدر نمی‌دهد، بلکه کارهای بی‌فایده را در کل خوشه (Cluster) تکثیر می‌کند. هرگز به تنهایی به پنل سبز مصرف سخت‌افزار اعتماد نکنید؛ زمان بیکاری با بودجه زمانی یکی نیست.

به نقل از گزارش dev.to، این رویکرد در زمان طوفان‌های سیستمی سه هزینه ایجاد می‌کند:

  • اتلاف توکن: ارسال تکراری درخواست‌ها با یک شناسه واحد، بدون ایجاد ارزش، توکن (Token) — تکه‌های کوچکی از متن، شبیه برش‌های یک کیک طولانی که مدل تکه‌تکه می‌خورد — مصرف می‌کند. شمارنده‌های توکن حتی زمانی که کاربر هیچ پاسخی دریافت نکرده است، جلو می‌روند. این اتلاف منابع شباهت زیادی به فرآیندهای زامبی در سیستم‌های استریمینگ دارد که توکن‌های AI را بدون دلیل می‌سوزانند.
  • رشد صف: هر تکرار، سن صف را برای تمام کپی‌های منتظر دیگر افزایش می‌دهد. هر نسخه تکثیر شده همچنان باید پشت همان صف پذیرش منتظر بماند.
  • خستگی اپراتور: مهندسان ساعت‌ها وقت صرف ردیابی کارگری می‌کنند که ظاهرش سالم است اما در واقع در کارهای دیرشده غرق شده است.

میزان مصرف می‌تواند در تمام این سه هزینه پایین بماند. همین تضاد است که باعث شد هشدار ساعت ۲:۱۴ فعال شود. هسته‌های بیکار در کنار سن بالای صف یعنی «رد کردن»، نه «تکرار».

پیاده‌سازی پذیرش مبتنی بر سن

برای حل این مشکل، معماری پیشنهادی منطق تصمیم‌گیری را به لبه‌ی پذیرش منتقل می‌کند. سیستم به‌جای نگاه به میزان مصرف، باید سیگنال‌های مشخصی را ارزیابی کند. پیش از هر درخواست POST بعدی، باید پرسید کدام سیگنال باید سد راه تکرار باشد: سن صف، میزان مصرف یا مهلت زمانی باقی‌مانده.

داده‌های کلیدی برای ثبت

برای ساخت این سد، باید این فیلدها را از یک پنجره استخراج کنید:

  • queue_age_ms در لبه پذیرش
  • worker_util در کپی سرویس‌دهنده
  • deadline_slack_ms در درخواست ورودی
  • retry_count در کلاینت یا Sidecar
  • token_units مصرف شده برای این شناسه
  • http_status آخرین تلاش upstream

اگر سن صف از مهلت باقی‌مانده بیشتر شد، متوقف شوید. هسته‌های بیکار را برای کاری که دیر شده هزینه نکنید. یک کپی رایگان، زمان واقعی از دست رفته را باز نمی‌گرداند. اولویت را به ترتیب قرار دهید: اول سن، دوم مهلت و در آخر میزان مصرف. مصرف تنها یک راهنما برای ظرفیت است، نه مجوزی برای پردازش کارهای دیرشده. کارهای دیرشده با بیکار بودن هسته‌ها، ارزان نمی‌شوند.

ماتریس تصمیم‌گیری برای تکرارها

برای کسانی که این منطق را در یک پروکسی یا کنترل‌کننده پذیرش پیاده می‌کنند، قوانین زیر حاکم است. این قانون را در پروکسی بنویسید، نه در یک صفحه ویکی، و پیش از ادغام (Merge)، منطق آستانه‌ها را با صدای بلند بیان کنید:

سن صف در برابر مهلت مصرف Worker تعداد تکرار اقدام
سن کمتر از نصف مهلت هر مقداری ۰ پذیرش یک‌باره
سن کمتر از مهلت مصرف زیر ۰.۳۰ ۰ پذیرش یک‌باره، بدون تکرار
سن برابر یا بیشتر از مهلت بیکار یا مشغول هر مقداری رد کردن، عدم تکرار
هر سن دیرشده‌ای هر مقداری ۱ یا بیشتر رد کردن و تخلیه

مصرف پایین هرگز اولویتش بیشتر از سن صف نیست. کلاینت مهلت خود را مصرف کرده و هسته‌های بیکار نمی‌توانند مهلت جدیدی برای کلاینت خلق کنند.

اعتبارسنجی محلی و تست

نویسنده برای اثبات این سازوکار بدون سوزاندن توکن‌های گران‌قیمت GPU (واحد پردازش گرافیکی)، یک تمرین محلی با Worker جعلی را پیشنهاد می‌کند. شما به یک خوشه GPU پولی نیاز ندارید. توپولوژی ساده است: client -> retry_proxy -> admission -> fake_worker. در این مدل، Worker جعلی می‌خوابد و سپس توکن برمی‌گرداند. پروکسی در صورت Timeout یا خطای ۵۰۳ تکرار می‌کند و بخش پذیرش زمانی که سن صف از مهلت بیشتر شود، درخواست را رد می‌کند.

شرایط تست اعلام‌شده

برای اطمینان از تنظیم درست متغیرها در آزمایشگاه، این مقادیر را در نظر بگیرید. این‌ها پیچ‌های تنظیم آزمایشگاهی هستند، نه بنچمارک‌های محصول یا ادعاهای فروشنده درباره نرخ تراکم:

  • پرامپت: یک پرامپت مصنوعی با هدف ۲۵۶ توکن
  • مهلت کلاینت: ۳۰۰۰ میلی‌ثانیه
  • خواب Worker در حالت عادی: ۲۵۰۰ میلی‌ثانیه
  • خواب در حالت تزریق خطا: ۴۰۰۰ میلی‌ثانیه
  • تکرارهای کلاینت: بدون سقف (تا ۴ تلاش)
  • قانون پذیرش: رد کردن در صورت بیشتر بودن سن نسبت به مهلت

اگر SLO شما از ثانیه استفاده می‌کند، این برچسب‌ها را تغییر دهید.

خط زمانی تمرین شکست

برای تطبیق لاگ‌ها با ساعت، این روند را دنبال کنید:

  • T+0 ms: کلاینت شناسه lab-1 را با مهلت ۳۰۰۰ میلی‌ثانیه ارسال می‌کند.
  • T+2500 ms: در حالت عادی پاسخ می‌آمد؛ اما در مسیر خطا، Worker هنوز در خواب است.
  • T+3000 ms: مهلت به صفر می‌رسد؛ مصرف هنوز ۰.۱۲ است.
  • T+4000 ms: Worker خطای ۵۰۳ یا یک پاسخ دیرشده برمی‌گرداند.
  • T+4001 ms: کلاینت کورکورانه اولین تکرار را زمان‌بندی می‌کند.
  • T+4001 ms: بخش پذیرش باید درخواست را رد کند؛ چون سن صف از مهلت پیشی گرفته است.

اگر پروکسی شما در T+4001 درخواست را بپذیرد، تمرین شکست خورده است. پیش از آنکه کسی را بیدار کنید، پرچم تکرار را به حالت قبلی برگردانید.

ابزار محلی قابل بازتولید

فایل زیر را با نام retry_admission_drill.py روی لپ‌تاپ خود ذخیره کنید. آن را فقط در برابر Worker جعلی محلی اجرا کنید و به مسیرهای تولید مشترک متصل نکنید.

#!/usr/bin/env python3
import json
import time
from dataclasses import dataclass

DEADLINE_MS = 3000
WORKER_SLEEP_MS = 2500
FAULT_SLEEP_MS = 4000
MAX_CLIENT_RETRIES = 4

@dataclass
class Probe:
    req_id: str
    retry_count: int
    enqueued_at: float
    deadline_ms: int

def queue_age_ms(p: Probe) -> float:
    return (time.monotonic() - p.enqueued_at) * 1000.0

def deadline_slack_ms(p: Probe) -> float:
    return p.deadline_ms - queue_age_ms(p)

def admit(p: Probe) -> str:
    age = queue_age_ms(p)
    slack = deadline_slack_ms(p)
    if p.retry_count >= 1 and slack <= 0:
        return 'reject_retry_late'
    if age >= p.deadline_ms:
        return 'reject_age_beats_slack'
    return 'admit_once'

def fake_worker(fault: bool) -> dict:
    sleep_ms = FAULT_SLEEP_MS if fault else WORKER_SLEEP_MS
    time.sleep(sleep_ms / 1000.0)
    return {'ok': not fault, 'sleep_ms': sleep_ms, 'token_units': 64}

def run_client(fault: bool, cap_retries: bool) -> dict:
    p = Probe('lab-1', 0, time.monotonic(), DEADLINE_MS)
    events = []
    attempts = 1 if cap_retries else MAX_CLIENT_RETRIES
    for i in range(attempts):
        p.retry_count = i
        decision = admit(p)
        events.append({
            'attempt': i,
            'decision': decision,
            'queue_age_ms': round(queue_age_ms(p), 1),
            'deadline_slack_ms': round(deadline_slack_ms(p), 1),
            'worker_util_hint': 0.12,
        })
        if decision.startswith('reject'):
            break
        result = fake_worker(fault)
        events.append({'worker': result})
        if result['ok']:
            break
    return {'fault': fault, 'cap_retries': cap_retries, 'events': events}

if __name__ == '__main__':
    print(json.dumps({
        'uncapped_fault': run_client(True, False),
        'capped_fault': run_client(True, True),
    }, indent=2))

خروجی مورد انتظار باید رد شدن درخواست‌ها را پس از صفر شدن مهلت نشان دهد. خطاهای بدون سقف، همان شناسه Probe را تکثیر می‌کنند، در حالی که خطاهای سقف‌دار در اولین نمونه سن دیرشده متوقف می‌شوند. اگر حالت بدون سقف همچنان در مهلت منفی پذیرش می‌کند، پیش از دست زدن به هر سرور راه دور، پروکسی را اصلاح کنید.

مدیریت خطاهای HTTP 429 و 503

کدهای خطای استاندارد اغلب اپراتورها را به اشتباه می‌اندازند تا تکرار کنند. خطای ۴۲۹ (Too Many Requests) یعنی Worker در حال محافظت از خود است و خطای ۵۰۳ (Service Unavailable) یعنی مسیر برای کپی‌های بیشتر ناامن است. هیچ‌کدام از این وضعیت‌ها، مهلتی که سپری شده را باز نمی‌گردانند.

بازپخش کورکورانه بعد از این خطاها، فقط کار را روی یک کپی در حال مرگ یا یک صف پر تکثیر می‌کند. هر دو باید توسط همان قانون «سن در برابر مهلت» محدود شوند تا از گسترش «طوفان تکرار» جلوگیری شود. وضعیت خطا را در کنار retry_count برای تحلیل‌های پس از حادثه (Postmortem) ثبت کنید؛ آن‌ها را در یک سبد تکرار واحد ادغام نکنید، زیرا وضعیت بدون سن، شما را دوباره به دنبال CPUهای بیکار می‌فرستد.

تحلیل: تغییر ذهنیت عملیاتی

این رویکرد نشان‌دهنده چرخش از نظارت «ظرفیت‌محور» به نظارت «زمان‌محور» است. در سرویس‌های وب سنتی، یک هسته آزاد یک فرصت است. اما در استنتاج مدل‌های زبانی بزرگ، جایی که تولید توکن زمان‌بر است و مهلت‌ها سخت‌گیرانه هستند، هسته آزاد اگر درخواست دیر شده باشد، بی‌ارزش است. این نگاه به مدیریت بودجه زمانی، مشابه معیاری است که در سنجش توسعه‌دهندگان عامل‌ها از طریق بودجه حلقه‌ها مورد تأکید قرار گرفته است.

عملیات هزینه باید مجموع توکن‌ها را به ازای هر شناسه حساب کند، نه به ازای هر پاسخ موفق. اگر یک شناسه چهار برابر توکن مصرف کرده، یعنی شما آن را تکثیر کرده‌اید. مصرف پایین این تکثیر را نشان نمی‌دهد، اما سن صف و تعداد تکرار، آن را افشا می‌کنند. برای شما این بدان معناست که «مصرف پایین» در زمان قطعی، دیگر نشانه سلامت نیست، بلکه اغلب نشانه شکست سیستمی در کنترل پذیرش است.

تزریق خطا و بازگشت (Rollback)

خطاها را به این ترتیب تزریق کنید: خواب Worker را بیش از مهلت زمانی افزایش دهید، مصرف را روی ۱۲٪ نگه دارید و تماشای کنید که سن صف از مهلت عبور می‌کند. تایید کنید که پذیرش هر تکرار بعدی را رد می‌کند و شناسه‌هایی که سن آن‌ها از مهلت بیشتر شده را تخلیه کنید.

اگر این پروکسی ترافیک واقعی را مدیریت می‌کند، مسیر بازگشت را این‌گونه طی کنید:
۱. مقدار RETRY_MAX=0 را فوراً در Sidecar کلاینت تنظیم کنید.
۲. پذیرش را برای مسیر affected روی reject_all قرار دهید.
۳. شناسه‌هایی که سن آن‌ها از مهلت بیشتر شده را تخلیه کنید.
۴. آخرین نسخه سالم را از کنترل نسخه بازیابی کنید.
۵. تنها زمانی مسیر را باز کنید که سن صف برای دو بازه استخراج، زیر مهلت بماند. برای مدیریت سریع‌تر اینگونه بحران‌ها در محیط عملیاتی، می‌توان از پروتکل ۶۰ دقیقه‌ای مهار خطاهای کدنویسی AI به عنوان یک چارچوب کمکی استفاده کرد.

مقیاس‌دهی به Workerهای آزاد برای جذب تکرارها اشتباه است؛ این کار سیگنال سن صف را می‌پوشاند. تنها پس از توقف حلقه تکثیر، کپی‌های جدید اضافه کنید.

محیط استقرار و محدودیت‌ها

برای اثبات این محدودیت در محیطی ارزان، MonkeyCode دسترسی رایگان به مدل و سرور ارائه می‌دهد (افشا: این مقاله به عنوان بخشی از معرفی محصولات MonkeyCode تهیه شده است). از این محیط فقط برای استقرار اولیه استفاده کنید، نه برای برنامه‌ریزی ظرفیت تولید. ترافیک پولی را پشت همان سقف تکرار نگه دارید و سن صف، تعداد تکرار و مهلت باقی‌مانده را با هم ثبت کنید.

این قانون پذیرش را برای موارد زیر نادیده بگیرید:

  • مسیرهای سخت-زمانی (Hard real-time) که هرگز صف تشکیل نمی‌دهند.
  • کارهای دسته‌ای (Batch) با پنجره‌های زمانی شبانه.
  • توزیع‌های Idempotent که از قبل شناسه‌ها را حذف تکرار می‌کنند.
  • تیم‌هایی که امکان استخراج queue_age_ms را ندارند.

اگر نمی‌توانید سن را اندازه بگیرید، حدس نزنید. حدس زدن، ظرفیت آزاد را به یک طوفان تکرار خاموش تبدیل می‌کند. این آزمایش کیفیت واقعی مدل را نمی‌سنجد و ادعای نرخ تراکم برای هیچ فروشنده‌ای ندارد. خواب Worker جایگزین تأخیر سرویس‌دهی است و توکن‌ها شمارنده‌های لاگ هستند، نه صورت‌حساب. همچنین جدول تصمیم‌گیری، عدالت بین مستاجران مشترک (Tenants) را نادیده می‌گیرد؛ اگر یک استخر Worker مشترک دارید، بودجه‌های هر کلید را اضافه کنید.

گام‌های بعدی برای پیاده‌سازی

مهندسان باید با تجهیز لبه‌های خود برای استخراج queue_age_ms و deadline_slack_ms شروع کنند. پس از مشاهده تله‌متری، یک بلوک پیکربندی کوچک بنویسید:

admission:
  reject_if_queue_age_ms_gte: 3000
  reject_if_retry_count_gte: 1
  ignore_worker_util_when_slack_ms_lte: 0

سپس بر اساس سن صف هشدار (Page) دریافت کنید، نه بر اساس هسته‌های بیکار. وقتی مصرف پایین و سن بالا است، رد کنید. زمان بیکاری Workerهای آزاد همچنان شرط‌بندی غلطی برای هزینه است. اولین آستانه خود را انتخاب کنید — سن صف، تعداد تکرار یا مهلت باقی‌مانده — و با تمرین محلی، رد شدن درخواست را اثبات کنید.

اما داستان سخت‌افزاری این تحول حتی شگفت‌انگیزتر است — به تحلیل ما درباره‌ی تراشه‌های Blackwell مراجعه کنید.

چرا این موضوع مهم است؟

این تغییر پارادایم در مدیریت زیرساخت، از اتلاف میلیاردها توکن در طوفان‌های تکرار جلوگیری می‌کند. تخصص در مدیریت زمانِ درخواست‌ها (Time-centric monitoring) اکنون برای پایداری سرویس‌های AI حیاتی‌تر از صرفاً افزایش تعداد GPUهاست.

تأثیر برای ایران

برای توسعه‌دهندگان ایرانی که با محدودیت منابع GPU و هزینه‌های بالای API مواجه‌اند، پیاده‌سازی این منطق در لایه پروکسی می‌تواند هزینه‌های استنتاج را به‌شدت کاهش دهد.

·نگاه ما
تحریریه دات‌هوش

این رویکرد نشان می‌دهد که در عصر مدل‌های زبانی، «زمان» به جای «محاسبات» به منبع اصلی و محدود تبدیل شده است. در حالی که اکثر تیم‌های DevOps هنوز بر اساس اشباع سخت‌افزاری (Utilization) تصمیم می‌گیرند، حقیقت این است که در استنتاج، یک درخواست دیرشده عملاً یک درخواست شکست‌خورده است. حذف تکرارهای بی‌فایده نه تنها هزینه توکن را کاهش می‌دهد، بلکه از فروپاشی آب‌شاری (Cascading Failure) کل خوشه جلوگیری می‌کند.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.