PagedAttention: ليه خدمة الـ LLM بتهدر معظم ذاكرة الـ GPU
يتطلب مستوى: محترف. المقال ده مفيد لو انت بتنشر نماذج لغوية في الإنتاج، وبتتعامل مع inference servers و GPU memory و batching.
لو بتخدم Llama أو أي LLM على GPU واحد وبتقدر تخدم 10 طلبات متزامنة بس، المشكلة غالبًا مش حجم الـ GPU. المشكلة إنك بتهدر 60 إلى 80% من ذاكرته على الفاضي. PagedAttention بتنزّل التهدير ده لأقل من 4% وترفع الإنتاجية حتى 24 ضعف على نفس الكارت. ركّز في السبب، لأنه مش زيادة في العتاد، ده تغيير في طريقة إدارة الذاكرة اتسرقت بالظبط من نظام التشغيل.
المشكلة باختصار: الـ KV Cache بياكل الذاكرة
لما الـ LLM بيولّد توكن جديد، بيحتاج يرجع لكل التوكنز اللي قبله. عشان ميعيدش حساب نفس الحاجة كل خطوة، بيخزّن نواتج وسيطة اسمها KV cache (مفاتيح وقيم طبقات الـ attention). الكاش ده بيكبر مع كل توكن جديد، وبيتحجز لكل طلب على حدة.
الرقم اللي بيوجع: في نموذج بحجم 13B، الـ KV cache بتاع توكن واحد بياخد حوالي 800 كيلوبايت. يعني تسلسل بطول 2048 توكن ممكن يوصل لـ 1.6 جيجابايت لطلب واحد. على A100 بسعة 40GB، ده بياكل جزء كبير من الذاكرة بعد ما النموذج نفسه ياخد نصيبه (المصدر: ورقة PagedAttention).
مثال يقرّب الفكرة قبل التشريح العلمي
تخيّل فندق بيحجز لكل نزيل جناح كامل من 20 أوضة، لأنه ممكن يجيب معاه 20 ضيف. أغلب النزلاء بييجوا لوحدهم، فـ 19 أوضة تفضل مقفولة وفاضية طول الإقامة. الفندق هيقولك مفيش أوضة فاضية وهو نص أوضه مقفول على الفاضي.
ده بالظبط اللي بيحصل في السيرفرات التقليدية. بتحجز لكل طلب مساحة متّصلة بحجم أطول إجابة ممكنة (مثلًا 2048 توكن)، حتى لو الإجابة طلعت 30 توكن. الباقي محجوز ومضيّع. النوع ده من الهدر اسمه internal fragmentation، وبيتجمع معاه external fragmentation وover-reservation (حجز مقدّم لتوكنز لسه ماتولّدتش).
الحل: هات فكرة الـ Virtual Memory من نظام التشغيل
نظام التشغيل حلّ نفس المشكلة من عشرات السنين. بدل ما يدّي كل برنامج كتلة ذاكرة متّصلة، بيقسّم الذاكرة لـ صفحات صغيرة ثابتة الحجم، وبيوزّعها على البرامج عند الحاجة، ويربطها بجدول صفحات. النتيجة: مفيش هدر يُذكر، وكل برنامج بيشوف ذاكرته كأنها متّصلة وهي في الحقيقة مبعثرة.
PagedAttention بتعمل نفس الحركة على الـ KV cache. بتقسّم كاش كل تسلسل لـ بلوكات، كل بلوك بيحمل عدد ثابت من التوكنز (الافتراضي 16). البلوكات دي مش لازم تكون متجاورة في الذاكرة الفعلية. جدول بلوكات بيربط التسلسل المنطقي بالبلوكات الفيزيائية المبعثرة. التهدير بينزل لجزء واحد بس: آخر بلوك ممكن يكون نص مليان، يعني أقل من 4% في المتوسط (المصدر: ورقة PagedAttention ومدوّنة vLLM).
وفوق ده، البلوكات اللي محتواها واحد (زي الـ prompt المشترك في عدة طلبات) ممكن تتشارك بنفس آلية الـ copy-on-write في نظام التشغيل. ده بيوفّر ذاكرة إضافية في سيناريوهات الـ parallel sampling.
الكود: شغّل vLLM وقيس الفرق بنفسك
vLLM هو المحرّك اللي طبّق PagedAttention. تشغيله سيرفر متوافق مع OpenAI API بيتعمل في أمر واحد:
# تثبيت
pip install vllm
# تشغيل سيرفر inference متوافق مع OpenAI API
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--gpu-memory-utilization 0.90 \
--max-num-seqs 256 \
--block-size 16 \
--enable-prefix-caching
الأعلام المهمة هنا:
- gpu-memory-utilization: نسبة ذاكرة الكارت المسموح لـ vLLM يستغلها للـ KV cache. كل ما زادت، يقدر يخدم batch أكبر، بس بتقترب من خطر الـ OOM.
- max-num-seqs: أقصى عدد تسلسلات في الـ batch الواحد. ده اللي بيحدّد سقف التزامن.
- block-size: عدد التوكنز في البلوك. القيمة الأصغر بتقلّل الهدر الداخلي بس بتزوّد overhead إدارة الجدول.
- enable-prefix-caching: بيعيد استخدام بلوكات الـ prompt المشترك بين الطلبات، مفيد جدًا في الـ RAG و الـ system prompts الطويلة.
ولو عايز تستخدمه برمجيًا offline للـ batching بدل السيرفر:
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
gpu_memory_utilization=0.90,
max_num_seqs=256,
)
prompts = ["summarize PagedAttention in one line."] * 1000
params = SamplingParams(temperature=0.7, max_tokens=128)
outputs = llm.generate(prompts, params)
print(len(outputs))
الميزة هنا مش بس الذاكرة. لمّا التهدير بيقلّ، تقدر تحطّ تسلسلات أكتر في نفس الـ batch، والـ continuous batching بيملأ الأماكن الفاضية فور ما طلب بيخلّص بدل ما يستنى الـ batch كله. النتيجة المركّبة هي قفزة الإنتاجية.
الأرقام اللي بيحصل فعلاً
على نفس النموذج ونفس الـ GPU وتحت حِمل عالٍ، فريق vLLM قاس إنتاجية أعلى تصل لـ 24× مقارنة بـ HuggingFace Transformers وحتى 3.5× مقارنة بـ HuggingFace TGI. الفضل الأساسي إن هدر الذاكرة نزل من 60–80% لأقل من 4%، فالـ batch الفعّال بقى أكبر بكتير (المصدر: مدوّنة vLLM وورقة PagedAttention).
سيناريو واقعي: لو عندك A100 80GB بتخدم Llama-3.1-8B لخدمة chatbot، السيرفر التقليدي ممكن يسقّفك عند بضع عشرات من الطلبات المتزامنة قبل ما الذاكرة تخلص. بنفس الكارت، vLLM بيقدر يدفع المئات بسبب البلوكات المتشاركة وقلة الهدر. ده الفرق بين سيرفر واحد وأربع سيرفرات لنفس الحِمل، أي توفير مباشر في الفاتورة.
الـ trade-offs: مفيش حاجة ببلاش
- تعقيد الـ kernel. الذاكرة المبعثرة بتتطلب attention kernel مخصّص يقرأ من بلوكات غير متجاورة. ده overhead بسيط في الـ kernel نفسه، بس المكسب الصافي في الإنتاجية بيغطّيه بكتير.
- ضبط block-size. بلوك أصغر = هدر داخلي أقل بس جداول أكبر و overhead إدارة أعلى. 16 قيمة افتراضية متوازنة، لكن مش مثلى لكل الأحمال.
- خطر الـ OOM. رفع gpu-memory-utilization لـ 0.95+ بيزوّد التزامن، بس أي spike في أطوال التسلسل ممكن يفجّر الذاكرة. الافتراض إن أحمالك متوقّعة نسبيًا.
- prefix caching بياكل ذاكرة. البلوكات المتشاركة بتفضل محجوزة طول ما ليها مرجع، فهو مكسب في الحالات المتكررة وعبء في الأحمال العشوائية تمامًا.
متى لا تستخدم هذه الطريقة
PagedAttention والـ continuous batching بيلمعوا تحت تزامن عالٍ. لو حالتك واحدة من دول، المكسب هيكون هامشي أو معدوم:
- طلب واحد في المرة (batch=1) بزمن استجابة حسّاس جدًا للـ single-stream. هنا الـ batching مش بيشتغل أصلًا.
- تسلسلات قصيرة جدًا وثابتة الطول، فالهدر الأصلي بسيط ومفيش كتير تكسبه.
- النموذج لا يسع في الذاكرة حتى بعد الـ quantization، فمشكلتك مشكلة سعة مش إدارة.
الخطوة التالية
شغّل النموذج بتاعك على vLLM مرة بـ --max-num-seqs 256 ومرة بقيمة أقل، وقيس الـ throughput بأداة زي vllm bench serve. بُص على عدّاد GPU KV cache usage في اللوج: لو قريب من 90% والـ throughput بيزيد مع رفع max-num-seqs، يبقى انت في النطاق اللي PagedAttention بتتألق فيه. لو لقيت OOM، نزّل gpu-memory-utilization خطوة وارجع قيس.
المصادر
- Kwon et al., "Efficient Memory Management for Large Language Model Serving with PagedAttention", arXiv:2309.06180, 2023 — arxiv.org/abs/2309.06180.
- vLLM Blog, "vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention" — blog.vllm.ai.
- vLLM Documentation — docs.vllm.ai.
- Red Hat Developer, "How PagedAttention resolves memory waste of LLM systems", 2025 — developers.redhat.com.