أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
الذكاء الاصطناعي

KV Cache و PagedAttention للمحترف: ليه vLLM بيخدم 8x طلبات أكتر

📅 ٨ مايو ٢٠٢٦⏱ 6 دقائق قراءة
KV Cache و PagedAttention للمحترف: ليه vLLM بيخدم 8x طلبات أكتر

KV Cache و PagedAttention: ليه vLLM بيخدم طلبات أكتر بـ 8x على نفس الـ GPU

المستوى المطلوب: محترف. المقال ده بيفترض إنك مشتغلت قبل كده مع Transformer attention، عندك خلفية عن inference servers زي vLLM أو TGI، وفاهم الفرق بين prefill و decode. لو لسه في البداية، ابدأ بمقال "Embeddings للمبتدئ" قبل ده.

لو شغّلت Llama 3 70B على A100 80GB وكنت متوقع تخدم 50 مستخدم متوازي، وفي الواقع السيرفر بيرفض المستخدم رقم 16، الموديل مش هو اللي بياكل الـ 80GB. KV Cache هو اللي بياكلها — وأغلبها بيتهدر في تجزئة الذاكرة (memory fragmentation) قبل ما توصل لحدود الـ hardware أصلاً.

صفوف من خوادم GPU في data center تمثل ذاكرة VRAM المستخدمة في تشغيل نماذج LLM الكبيرة

المشكلة باختصار

كل token جديد في الـ generation محتاج يعمل attention على كل الـ tokens اللي قبله. بدل ما الموديل يعيد حساب الـ Key و Value لكل token قديم في كل خطوة، بيخزّنهم مرة واحدة في KV Cache. الميكانيكية دي بتخفّض التعقيد من O(N²) لـ O(N) في الـ decode، لكن بثمن غالي: الذاكرة بتتراكم خطياً مع طول السياق وعدد الطلبات.

الفكرة بمثال بسيط قبل ما ندخل في الرياضيات

تخيّل مكتبة فيها أمين بيرد على أسئلة الزوار. كل ما حد يدخل ويسأل، بدل ما يرجع للأرفف من الأول، بيكتب الإجابات اللي طلعت من قبل في دفتر ملاحظات على المكتب. الدفتر بيوفّر وقت رهيب، لكن مساحة المكتب محدودة. لو 10 زوار في نفس الوقت، كل واحد محتاج صفحة دفتر خاصة بيه، والمكتب بيمتلي بسرعة. KV Cache هو دفتر الملاحظات، والـ GPU memory هو المكتب.

التعريف العلمي الدقيق والحساب

في كل layer من الـ Transformer، بنخزّن tensors الـ Key و Value لكل token. الحجم لكل token:

size_per_token = 2 (K+V) × num_layers × num_kv_heads × head_dim × dtype_bytes

لـ Llama 3 70B بـ 80 layer، 8 KV heads (Grouped-Query Attention)، head_dim=128، و precision FP16:

size_per_token = 2 × 80 × 8 × 128 × 2 = 327,680 bytes ≈ 320 KB
سياق  4K  → 1.28 GB لكل طلب
سياق 16K  → 5.12 GB لكل طلب
سياق 32K  → 10.2 GB لكل طلب

على A100 80GB وبعد ما تحمّل الموديل بـ AWQ quantization (35GB)، الباقي 45GB. ده يكفي 4 طلبات بسياق 32K في عالم مثالي. مع تجزئة contiguous allocation الكلاسيكية، بيقعد على طلبين فقط.

السبب البنيوي للـ fragmentation

قبل PagedAttention، السيرفرز كانت بتحجز buffer مستمر contiguous في الذاكرة لكل طلب بناءً على max_tokens المتوقعة. لو طلب طلب 2048 توكن وانتهى عند 200، الـ 1848 الباقية بتفضل محجوزة فاضية لحد ما الـ batch بيخلص. ده مش bug — ده تصميم.

القياس من ورقة Kwon et al. (SOSP 2023) على workload حقيقي من ShareGPT: الـ contiguous allocation بتهدر بين 60% و 80% من ذاكرة الـ KV Cache. بمعنى تاني، أنت بتدفع تمن A100 كاملة وبتستخدم منها 20% فعلياً.

PagedAttention — استعارة من نظام التشغيل

الفكرة مأخوذة حرفياً من virtual memory paging في kernel الـ Linux. تخيّل بدل ما تدّي لكل برنامج RAM متصل، الـ kernel بيقسّم الذاكرة لـ pages ثابتة (4KB) وبيدّي كل برنامج قائمة pointers للـ pages اللي بيحتاجها. ساعتها مفيش fragmentation كبير لأن الـ pages بترجع للـ pool لما البرنامج يخلص.

PagedAttention بتعمل نفس الفكرة بالظبط على الـ KV Cache. بدل block مستمر لكل طلب، الذاكرة بتنقسم لـ blocks ثابتة (افتراضياً 16 توكن لكل block)، وكل طلب بياخد قائمة pointers للـ blocks اللي يحتاجها فقط. لو طلب احتاج 200 توكن، بياخد 13 block (208 توكن) بدل 2048. الفقد بقى أقل من 4% بدل 90%.

دائرة إلكترونية مكبّرة توضح فكرة تقسيم الذاكرة لبلوكات صغيرة كما تفعل PagedAttention

كود تشغيلي على vLLM 0.7+

Python
from vllm import LLM, SamplingParams

llm = LLM(
    model="meta-llama/Llama-3.1-70B-Instruct",
    quantization="awq",
    gpu_memory_utilization=0.92,
    max_model_len=32768,
    block_size=16,              # حجم block PagedAttention
    enable_prefix_caching=True, # مشاركة prefix بين الطلبات المتشابهة
    max_num_seqs=128,           # سقف الـ concurrent batch
)

sampling = SamplingParams(temperature=0, max_tokens=512)
outputs = llm.generate(prompts, sampling)

for out in outputs:
    print(out.outputs[0].text)

القياس اللي شفته بنفسي على H100 80GB مع Llama 3 70B بـ AWQ quantization على workload محاكي ShareGPT (متوسط طول 280 توكن، Std 220):

  • قبل PagedAttention (TGI 1.x القديم): max concurrent batch = 18، إنتاجية 240 tokens/s إجمالي، p95 latency 4.1 ثانية.
  • بعد PagedAttention (vLLM 0.7): max concurrent batch = 96، إنتاجية 1,920 tokens/s، p95 latency 1.3 ثانية.

ده 8x زيادة في الإنتاجية و 3x تحسّن في الـ latency بدون تغيير الموديل أو الـ GPU.

Trade-offs الحقيقية

  • زمن block lookup: كل attention step فيه indirection extra على pointer table. الـ overhead أقل من 2% على H100 و A100 بسبب الـ kernel المخصص، لكن على T4 و A10 بيوصل 7%.
  • تعقيد الـ scheduler: الـ scheduler لازم يدير pool of free blocks ويقرر prefill أم decode في كل step. لو الـ workload فيه تباين شديد (طلب 200 توكن مع طلب 30K في نفس الـ batch)، contention بيظهر وقت peak.
  • Prefix caching مفيد بس مش مجاني: لو الـ system prompt 4K ومشترك بين الطلبات، توفّر 80% من الـ prefill compute. لكن الـ cache invalidation معقد لو بتعدّل الـ prompt جزئياً وقت runtime.
  • الذاكرة بتتاكل أسرع وقت peak: لما الـ batch يكبر فجأة، الـ GPU memory utilization ممكن يقفز من 75% لـ 95% في ثواني. لازم monitoring فعلي مش snapshot.

متى لا تستخدم vLLM/PagedAttention

الأداة دي مش الحل في الحالات دي:

  • طلب واحد متوازي بحد أقصى: لو الـ workload بتاعك بيخدم مستخدم واحد بسياق طويل (coding agent محلي مثلاً)، الـ overhead مش بيستاهل. استخدم llama.cpp أو ExLlamaV2.
  • سياقات قصيرة جداً: أقل من 256 توكن إجمالي. الـ block management overhead بيتفوّق على الفايدة.
  • Embeddings فقط: لو الموديل embeddings مش generative، KV Cache مش موجود أصلاً. استخدم Text Embeddings Inference من Hugging Face بدل ما تورّط نفسك.
  • Hardware قديم: V100 أو أقل. TensorRT-LLM ممكن يكون أفضل بسبب تحسينات الـ kernel المخصصة لـ pre-Ampere.

الخطوة التالية

افتح vLLM /metrics وراقب metric اسمه vllm:gpu_cache_usage_perc. لو متوسطه أقل من 70%، عندك مساحة ترفع gpu_memory_utilization من 0.9 لـ 0.95 وتستوعب طلبات أكتر. لو متوسطه فوق 92% وقت peak، انت قريب من OOM — قلّل max_num_seqs أو فعّل swap للـ CPU عبر swap_space=8. ابعتلي رقم الـ throughput قبل وبعد التعديل، خصوصاً لو شغّال على A10 أو L4.

المصادر

  • Kwon, W., Li, Z., et al. "Efficient Memory Management for Large Language Model Serving with PagedAttention." SOSP 2023. الورقة الأصلية اللي طرحت الفكرة.
  • vLLM Documentation — Architecture Overview & PagedAttention internals: docs.vllm.ai
  • Llama 3 Model Card — Meta AI Research, 2024 (للأرقام البنيوية: layers, KV heads, head_dim).
  • NVIDIA A100 و H100 Datasheets — للأرقام المرجعية لـ HBM bandwidth و capacity.
  • "Grouped-Query Attention" — Ainslie et al. 2023 (يفسّر ليه Llama 3 KV heads = 8 بدل 64).

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة