فاز 17 · درس 6

سرویس‌دهی با کش پیشوند: معماری RadixAttention و بازاستفاده از KV

نوع
مفهومی
زمان
حدود 75 دقیقه
زبان‌ها
Python (stdlib, toy radix-tree cache + cache-aware scheduler)
پیش‌نیاز
فاز 17 · بخش 04 (سازوکار داخلی موتور سرویس‌دهی)، فاز 14 (سیستم RAG ایجنتی)
درخت انتزاعی از حافظه گرافیکی با ساختار رادیکس و گره‌های نورانی که بلوک‌های مشترک داده را متصل نگه داشته‌اند
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم پایه
  4. درخت مبنا به عنوان نمایه‌ی KV
  5. زمان‌بندی آگاه از کش
  6. اعداد بنچمارک که باید به خاطر بسپارید
  7. نکته‌ی کلیدی در ترتیب پرامپت‌ها
  8. جاهایی که RadixAttention برنده یا بازنده است
  9. چرا این مسئله مربوط به اسکژولر است، نه فقط یک کرنل
  10. تقابل با vLLM
  11. استفاده در عمل
  12. خروجی این درس
  13. تمرین‌ها
  14. اصطلاحات کلیدی
  15. برای مطالعه‌ی بیشتر

در سیستم‌های مبتنی بر مدل‌های زبانی، به‌ویژه در کاربردهای RAG و ایجنت‌ها، بخش بزرگی از پرامپت‌ها مانند پرامپت سیستم و قالب اسناد بازیابی‌شده تکراری هستند. در سرویس‌دهنده‌های سنتی، این پیشوندهای تکراری در هر درخواست دوباره پردازش می‌شوند که پردازنده‌ی گرافیکی را به تکرار بیهوده‌ی فاز Prefill وا می‌دارد و ظرفیت خدمت‌رسانی را هدر می‌دهد.

موتور SGLang با معرفی معماری RadixAttention حافظه‌ی کش کلید-مقدار (KV Cache) را در قالب یک درخت مبنا (Radix Tree) سازماندهی می‌کند. با همراهی یک اسکژولر آگاه از کش، درخواست‌های دارای پیشوند مشترک شناسایی و شاخه‌های داغ در HBM حفظ می‌شوند تا هزینه و تأخیر توکن اول به شکل چشمگیری افت کند.

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

اگر به کش کلید-مقدار (KV cache) به چشم یک منبع تراز اول و قابل استفاده‌ی مجدد نگاه کنید که در یک درخت مبنا (Radix tree) ذخیره شده است، نحوه‌ی زمان‌بندی هم به‌کلی دگرگون می‌شود: به‌جای روش «ورود به نوبت» (FCFS یا First-Come, First-Served) که vLLM استفاده می‌کند، یک اسکژولر آگاه از کش اولویت را به درخواست‌هایی می‌دهد که پیشوندهای مشترک طولانی‌تری دارند — در عمل نوعی پیمایش اول-عمق روی درخت مبنا تا شاخه‌های داغ در HBM مقیم بمانند. SGLang موتوری است که سرویس‌دهی را بر پایه‌ی همین ایده بنا کرده است. روی مدل Llama 3.1 8B با 1K پرامپت مشابه ShareGPT، نرخ پردازش SGLang به حدود 16,200 توکن بر ثانیه می‌رسد، در حالی که vLLM روی حدود 12,500 توکن بر ثانیه می‌ایستد؛ یعنی تقریباً 29% برتری. روی بارهای کاری RAG با پیشوندهای سنگین، این مزیت تا 6.4 برابر افزایش می‌یابد. در بارهای کاری با ساختار شبیه‌سازی صدا (Voice Cloning)، نرخ اصابت کش از 86% گذشت. در سال 2026 روی بیش از 400,000 پردازنده‌ی گرافیکی در xAI، LinkedIn، Cursor، Oracle، GCP، Azure و AWS مستقر شده است. نکته‌ی ظریف اینجاست که در صورت رعایت‌نشدن ترتیب پیشوندها، آن رقم 6.4 برابری دود می‌شود و به هوا می‌رود؛ ترتیب پرامپت‌ها اهرم اصلی مهندس است.

اهداف یادگیری

  • رسم دیاگرام RadixAttention: چگونگی ذخیره‌ی پیشوندها در یک درخت مبنا و نحوه‌ی اشتراک‌گذاری بلاک‌های KV بین توالی‌هایی که ریشه در یک branch یکسان دارند.
  • توضیح زمان‌بندی آگاه از کش و این‌که چرا FCFS برای ترافیک با پیشوند سنگین اشتباه است.
  • محاسبه‌ی شتاب مورد انتظار برای یک بار کاری بر اساس نرخ اصابت کش پیشوند (Prefix Caching) و توزیع طول پرامپت.
  • نام‌بردن از قاعده‌ی چینش پرامپت که رقم 6.4 برابری را از یک فرصت بربادرفته به مزیتی واقعی تبدیل می‌کند.

صورت مسئله

سرویس‌دهی کلاسیک با پرامپت هر درخواست مثل یک جعبه‌ی مات برخورد می‌کند. حتی وقتی 5,000 درخواست RAG همگی با یک پرامپت سیستمِ 2,000 توکنی و مقدمه‌ی بازیابی یکسان شروع می‌شوند، vLLM آن پیشوند 2,000 توکنی را 5,000 بار پیش‌پرکنی (Prefill) می‌کند. پردازنده‌ی گرافیکی مدام همان کار تکراری را از اول انجام می‌دهد.

مشاهده (Observation): پرامپت‌ها در بارهای کاری ایجنت‌ها و RAG تقریباً همیشه پیشوندهای بلندی را به اشتراک می‌گذارند. پرامپت سیستم، اسکیمای ابزار (Tool Schema)، مثال‌های چندشات، هدرهای بازیابی و تاریخچه‌ی گفت‌وگو — همگی در میان درخواست‌ها تکرار می‌شوند. اگر کش کلید-مقدار را برای آن پیشوند یک بار ذخیره و دوباره استفاده می‌کردید، دیگر نیازی به پیش‌پرکنی مجدد آن نبود.

مکانیزم RadixAttention دقیقاً همین کار را می‌کند. توکن‌ها در یک درخت مبنا نمایه می‌شوند؛ هر گره مالک بلاک‌های KV برای توالی توکن‌های روی مسیر خود از ریشه است. درخواست جدید درخت را می‌پیماید: هر گره‌ای که توکنش تطابق داشته باشد، بلاک‌های KV همان گره را بازاستفاده می‌کند. هزینه‌ی پیش‌پرکنی متناسب با پسوند «جدید» می‌شود، نه کل پرامپت.

چالش اصلی زمان‌بندی است. اگر دو درخواست یک پیشوند 2,000 توکنی مشترک داشته باشند و درخواست سوم فقط در 200 توکن از همان پیشوند سهیم باشد، ترجیح می‌دهید آن دو درخواست با اشتراک طولانی را با هم پردازش کنید تا پیشوند طولانی در HBM باقی بماند. روش FCFS درست برعکس عمل می‌کند — به هر درخواستی که زودتر برسد سرویس می‌دهد و چه‌بسا شاخه‌ی داغ را پیش از رسیدن درخواست بعدیِ با پیشوند بلند، از حافظه بیرون بیندازد.

مفاهیم پایه

درخت مبنا به عنوان نمایه‌ی KV

یک درخت مبنا (Compact Trie) توالی‌های توکن را ذخیره می‌کند. هر گره مالک یک بازه‌ی توکنی و بلاک‌های KV محاسبه‌شده برای همان بازه است. فرزندان، این توالی را به اندازه‌ی یک یا چند توکن امتداد می‌دهند.

root
 |- "You are a helpful assistant..."  (2,000 tokens, 124 KV blocks)
      |- "Context: <doc A>..."        (500 tokens, 31 blocks)
           |- "Question: Alice..."    (80 tokens, 5 blocks)
           |- "Question: Bob..."      (95 tokens, 6 blocks)
      |- "Context: <doc B>..."        (520 tokens, 33 blocks)

درخواست جدیدی همراه با پرامپت سیستم + "Context: <doc A>" + "Question: Carol" از راه می‌رسد. اسکژولر درخت را می‌پیماید: پیشوند سیستم تطابق پیدا می‌کند (124 بلاک بازاستفاده می‌شود)، branch مربوط به doc-A تطابق پیدا می‌کند (31 بلاک بازاستفاده می‌شود)، سپس بلاک‌های جدید را فقط برای "Question: Carol" تخصیص می‌دهد (4 بلاک). هزینه‌ی پیش‌پرکنی: 4 بلاک برای توکن‌های جدید. بدون این درخت: 160 بلاک. یعنی حدود 40 برابر صرفه‌جویی در فاز پیش‌پرکنی.

زمان‌بندی آگاه از کش

بازاستفاده‌ی متکی بر درخت مبنا، در صورت تلاطم شدید کش (Churn)، بی‌فایده خواهد بود. دو سیاست کلیدی در اینجا وجود دارد:

  1. اعزام اول-عمق (Depth-first dispatch). هنگام انتخاب درخواست بعدی از صف، درخواست‌هایی را ترجیح دهید که ریشه در همان branch مجموعه‌ی در حال اجرای فعلی دارند. این کار شاخه‌ی داغ را در حافظه سنجاق‌شده (Pinned) نگه می‌دارد.
  2. سیاست LRU در سطح branch، نه سطح بلاک. به‌جای بلاک‌های مجزا، کل branchها را (با شروع از برگ‌هایی که کمترین استفاده را داشته‌اند) بیرون بیندازید تا ساختار کش با ساختار درخت مبنا همخوانی داشته باشد.

روش FCFS هر دو سیاست را نقض می‌کند. درخواستی با 2,000 توکن مشترک پشت سر درخواستی با 50 توکن مشترک معطل می‌ماند؛ سپس branch با 2,000 توکن از حافظه تخلیه می‌شود تا جا برای همان درخواست 50 توکنی باز شود.

اعداد بنچمارک که باید به خاطر بسپارید

  • مدل Llama 3.1 8B روی H100 با 1K پرامپت ShareGPT: نرخ پردازش SGLang حدود 16,200 توکن بر ثانیه در برابر vLLM با 12,500 توکن بر ثانیه (حدود 29% برتری).
  • بارهای کاری RAG با پیشوند سنگین (سیستم یکسان + سند یکسان، سوالات متغیر): تا 6.4 برابر سرعت بالاتر روی SGLang.
  • بارهای کاری شبیه‌سازی صدا: نرخ اصابت کش پیشوند 86.4%.
  • نرخ‌های اصابت در محیط عملیاتی (پروداکشن) میان مشتریان SGLang: بین 50 تا 99 درصد بسته به انضباط چینش پرامپت.
  • مستقر روی بیش از 400,000 پردازنده‌ی گرافیکی در سال 2026.

نکته‌ی کلیدی در ترتیب پرامپت‌ها

رقم 6.4 برابری به ثبات ترتیب در قالب پرامپت (Prompt template) وابسته است. اگر کلاینت شما پرامپت‌ها را در برخی درخواست‌ها به‌شکل [system, tools, context, history, question] و در برخی دیگر به‌شکل [system, context, tools, history, question] بسازد، درخت قادر به پیدا کردن پیشوند مشترک نخواهد بود. آنچه از دید انسان پیشوندی مشترک به نظر می‌رسد، برای درخت مبنا دو توالی کاملاً مجزا است.

اهرم مهندس: قالب پرامپت شما، کلید کش شماست. ترتیب را ثابت نگه دارید. همه‌ی بخش‌های تغییرناپذیر (سیستم، ابزارها، اسکیماها) را اول بگذارید. بافتار بازیابی‌شده (Context) را در رتبه‌ی بعد قرار دهید. سوال کاربر را آخر از همه بیاورید. محتوای پویا را لابلای پیشوند جا نزنید.

یک نمونه‌ی واقعی از دل پژوهش‌ها: بیرون کشیدن محتوای پویا از داخل پیشوندِ قابل کش، تنها با یک تغییر، نرخ اصابت کش را در یک پروداکشن از 7% به 74% رساند.

جاهایی که RadixAttention برنده یا بازنده است

بردها:

  • روش RAG (مقدمه‌ی بازیابی یکسان، سوال متغیر).
  • ایجنت‌ها (اسکیمای ابزار یکسان، کوئری متغیر).
  • چت با پرامپت سیستم طولانی.
  • بارهای کاری صوتی / تصویری با مقدمه‌های تکراری.

باخت‌ها (افت مجدد به سطح نرخ پردازش vLLM):

  • تولید تک‌شات با پرامپت‌های یکتا (تکمیل کد، چت‌های بدون چارچوب و فاقد پرامپت سیستم).
  • پرامپت‌های پویا که در آن‌ها هر درخواست محتوایی یکتا را لابه‌لای پیشوند می‌گنجاند.

چرا این مسئله مربوط به اسکژولر است، نه فقط یک کرنل

می‌توان بازاستفاده از KV را با یک ترفند کرنل (Kernel Trick) پیاده کرد. بینش SGLang این است که بازاستفاده تنها زمانی ثمر می‌دهد که اسکژولر، شاخه‌ی داغ را در حافظه مقیم نگه دارد. یک سیاست ساده‌لوحانه‌ی «در صورت وجود، بازاستفاده کن» زیر بار ترکیبی باعث تلاطم کش می‌شود. اسکژولری که با درخت مبنا نمایه شده، همان عاملی است که ترفند کرنل را به 29% برتری در پروداکشن بدل می‌کند.

تقابل با vLLM

این دو سیستم رقبای سرسخت و مطلقی نیستند. در سال 2026، vLLM قابلیت کش پیشوند (--enable-prefix-caching) و یک مسیریاب آگاه از کش (vLLM Router با زبان Rust) را به خود اضافه کرد. شکاف کمتر شد اما به‌طور کامل از بین نرفت — کل پشته‌ی SGLang از پایه بر مدار درخت مبنا بنا شده؛ در حالی که vLLM آن را روی ساختار قبلی خود سوار کرده است. برای بارهای کاری که بازاستفاده از پیشوند در آن‌ها حرف اول را می‌زند، SGLang همچنان گزینه‌ی پیش‌فرض است. برای سرویس‌دهی‌های عمومی بدون الگوهای پرپیشوند، vLLM برابر یا بهتر ظاهر می‌شود.

مدل ROOFLINEشدت محاسباتی را بکشید
ROOFLINEArithmetic intensity is the FLOPs a kernel does per byte it moves. On the left the slanted roof means performance is capped by memory bandwidth; on the right the flat roof means it is capped by raw compute. The ridge is where the two meet. A kernel below and left of the ridge (orange) is memory-bound, and the fix is more data reuse, not a faster chip.خط‌الرأس 125 FLOP/B
شدت محاسباتی (Arithmetic intensity) برابر است با تعداد FLOPهایی که یک کرنل به ازای هر بایت جابه‌جایی انجام می‌دهد. در سمت چپ، سقف شیب‌دار نشان می‌دهد عملکرد به پهنای باند حافظه محدود است؛ در سمت راست، سقف مسطح نشان می‌دهد محدودیت از توان محاسباتی خام ناشی می‌شود. خط‌الرأس (ridge) محل تلاقی این دو است. کرنلی که پایین و سمت چپ خط‌الرأس باشد (نارنجی) memory-bound است، و راه‌حل آن استفاده‌ی مجدد بیشتر از داده‌هاست، نه یک تراشه‌ی سریع‌تر.

استفاده در عمل

فایل code/main.py یک کش KV با درخت مبنای ساده به‌همراه یک اسکژولر با دو سیاست را پیاده‌سازی می‌کند: FCFS و آگاه از کش. این اسکریپت یک بار کاری یکسان را روی هر دو اجرا کرده و نرخ اصابت کش پیشوند و اختلاف نرخ پردازش را گزارش می‌دهد؛ سپس همان بار کاری را با «ترتیب درهم‌ریخته» اجرا می‌کند تا فروپاشی آن شتاب 6.4 برابری را نشان دهد.

شبیه‌ساز Inside an LLM Serving GPU عملکرد کش مبنا را روی یک اجرای واقعی با پردازنده‌ی L4 نشان می‌دهد. چهار گروه حضور دارند که هر کدام پرامپت سیستم 4,096 توکنی خود را دارند. اولین درخواست هر گروه، پرامپت خود را محاسبه می‌کند. تا زمانی که آن پرامپت در کش بماند، درخواست‌های بعدیِ آن گروه تنها سوال 128 توکنی را محاسبه می‌کنند. کش مبنا را خاموش کنید تا ببینید چگونه زمان میانه‌ی توکن اول از 479 میلی‌ثانیه به 14,706 میلی‌ثانیه می‌رسد.

خروجی این درس

این درس فایل outputs/skill-radix-scheduler-advisor.md را ایجاد می‌کند. این فایل با دریافت مشخصات یک بار کاری (ساختار قالب پرامپت، الگوی بازیابی، تعداد کاربران هم‌زمان)، دستورالعملی برای چینش پرامپت‌ها و توصیه‌ای مبنی بر شروع یا انصراف از به‌کارگیری SGLang ارائه می‌دهد.

تمرین‌ها

  1. اسکریپت code/main.py را اجرا کنید. عملکرد FCFS و اسکژولر آگاه از کش را روی یک بار کاری یکسان مقایسه کنید. این اختلاف کارایی از کجا ناشی می‌شود — صرفه‌جویی در پیش‌پرکنی (Prefill)، صرفه‌جویی در رمزگشایی (Decode)، یا تأخیر صف؟
  2. بار کاری را طوری تغییر دهید که جای اجزای [system, tools, context] در پرامپت‌ها به‌صورت تصادفی جابه‌جا شود. آن را دوباره اجرا کنید. چه بلایی سر نرخ اصابت می‌آید؟ چرا؟
  3. هزینه‌ی HBM برای مقیم نگه‌داشتن یک پرامپت سیستمِ 2,000 توکنی را به‌صورت یک branch درخت مبنا در مدل Llama 3.1 8B محاسبه کنید. آن را با هزینه‌ی یک بچ 16 توالی بدون بازاستفاده از پیشوند مقایسه کنید.
  4. مقاله‌ی SGLang RadixAttention را بخوانید. در سه جمله توضیح دهید که چرا تخلیه‌ی LRU با ساختار درختی، در بارهای کاری با پیشوند سنگین بر LRU در سطح بلاک برتری دارد.
  5. یکی از مشتریان گزارش می‌دهد که نرخ اصابت کش او تنها 8% است. سه علت احتمالی برای این موضوع نام ببرید و تست تشخیصی خود را برای هرکدام شرح دهید.

اصطلاحات کلیدی

اصطلاح در گفت‌وگوهای عامیانه معنای دقیق فنی
RadixAttention «همان فناوری SGLang» کش KV نمایه‌شده در قالب درخت مبنا تا پیشوندهای مشترک بلاک‌ها را بازاستفاده کنند
Radix tree «ترای فشرده» درختی که هر گره‌ی آن مالک یک بازه‌ی توکنی و بلاک‌های KV مربوط به آن است
Cache-aware scheduler «اول شاخه‌ی داغ» اسکژولری که اولویت را به درخواست‌های مشترک با branch مقیم می‌دهد
Prefix-cache hit rate «چقدر از پرامپت رایگان شد» کسری از توکن‌های پرامپت که از بلاک‌های KV بازاستفاده‌شده تأمین شدند
FCFS «اولین ورودی، اولین خروجی» زمان‌بندی پیش‌فرضی که محلی‌بودن پیشوند را از بین می‌برد
Branch-level LRU «برگ را بیرون بینداز» سیاست تخلیه‌ی حافظه متناسب با ساختار درختی مبنا
Prompt template ordering «کلید کش» ترتیب اجزای پرامپت تعیین می‌کند چه بخش‌هایی در درخت به اشتراک گذاشته شوند
System prompt pinning «پیشوند مقیم» پین‌کردن بخش تغییرناپذیر سیستم در حافظه برای جلوگیری از فرسودگی ناشی از تخلیه‌ی مداوم

برای مطالعه‌ی بیشتر

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسکدام ساختار داده‌ی اصلی، پایه‌ی استفاده‌ی مجدد از کش کلید-مقدار در SGLang است؟
  2. حین درسچرا زمان‌بندی FCFS برای بارهای کاری مبتنی بر پیشوند پرتکرار در SGLang نامناسب است؟
  3. حین درساسکژولر آگاه از کش در SGLang از چه دانه‌بندی تخلیه‌ای (eviction granularity) برای تطابق با شکل درخت رادیکس استفاده می‌کند؟
  4. حین درسمستقیم‌ترین اهرم مهندسی برای حفظ قابلیت شناسایی پیشوندهای مشترک در درخت رادیکس چیست؟
  5. پس از درسدرس انتظار ندارد RadixAttention در کدام الگوی کاری برتری داشته باشد؟
  6. پس از درسدیپلوی ProjectDiscovery چگونه نرخ برخورد کش پیشوند (prefix-cache hit rate) را از 7% به 74% رساند؟

سؤالات متداول

معماری RadixAttention چطور سرعت سرویس‌دهی مدل زبانی را بالا می‌برد؟

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

چرا الگوریتم زمان‌بندی FCFS برای ترافیک دارای کش پیشوند نامناسب است؟

روش FCFS درخواست‌ها را صرفاً بر اساس زمان ورود پردازش می‌کند که می‌تواند باعث بیرون‌افتادن (Eviction) شاخه‌های داغ دارای پیشوند بلند از حافظه GPU پیش از اتمام استفاده شود.

ترتیب اجزای پرامپت چه تأثیری در کارایی کش پیشوند دارد؟

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

ما رو توی گوگل بیشتر ببین

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