دورة حياة الريكويست في ال vLLM
المقال ده بيشرح بالتفصيل إزاي طلب الـ "Inference Request" بيمشي جوه محرك vLLM V1، وهو محرك "Inference" مفتوح المصدر معمُول عشان يخدم "Large Language Models" (LLMs) بكفاءة وعنطاق واسع.
الـ vLLM بيعتمد على "multi-process architecture" مكون من `AsyncLLM` و `EngineCore`، وده عشان يعدي أو يتخطى الـ "Global Interpreter Lock" (GIL) بتاع بايثون. ده بيعلي الـ "throughput" وبيخلي المهام اللي بتستهلك الـ "CPU" كتير (زي الـ "tokenization" والتعامل مع الـ "API") تشتغل في نفس الوقت مع تنفيذ النموذج اللي بيستهلك الـ "GPU" كتير.
طلب الـ "inference" بيبدأ لما الـ "client" بيبعت طلب "HTTP" متوافق مع "OpenAI" لسيرفر الـ "API" بتاع الـ vLLM. مكون الـ `AsyncLLM` بيستقبل الطلب ده، وبيعمل "tokenization" للـ "prompt"، وبعد كده بيبعتّه بشكل غير متزامن للـ `EngineCore` عن طريق "Inter-Process Communication" (IPC) باستخدام الـ `AsyncMPClient`.
جوه الـ `EngineCore`، الـ `Scheduler` بينظم الطلبات باستخدام خوارزمية اسمها "continuous batching". الطريقة دي مهمة جدًا عشان تزود استغلال الـ "GPU" لأقصى درجة، وده عن طريق إنه بيعالج طلبات كتير في "single forward pass" واحدة، مع الالتزام بميزانية `max_num_batched_tokens` اللي ممكن تتظبط، وبيضمن العدل بين الطلبات. الـ "Scheduler" ده بيدير كذا حاجة: "waiting deque" (دي قائمة بالطلبات الجديدة أو اللي وقفت شوية)، وكمان "running list" (دي للطلبات اللي شغالة وبتولد الردود دلوقتي).
الـ `KVCacheManager` ده أساسي في إدارة ذاكرة الـ "GPU" بكفاءة عالية. بيتعامل مع "Key (K)" و "Value (V)" tensors بتاعة الـ "Transformer" (اللي هي الـ "KV Cache") كإنها "paging system"، بحيث بيخصص "KV Blocks" بحجم ثابت وبشكل ديناميكي في ذاكرة الـ "GPU" لكل طلب. الـ "IDs" (المعرفات) بتاعة الكتل دي بتتربط بالطلب، وبعد كده الـ `ModelRunners` بتستخدمها.
الـ `ModelExecutor`، اللي مبني على مكتبة "Ray" للحوسبة الموزعة، بيوزع الطلبات المجمعة ("batched requests") على الـ `ModelRunners` اللي بتشتغل على الـ "GPU workers" كل واحد لوحده. في مرحلة الـ `prefill`، كل الـ "prompt tokens" من طلب معين بتتعالج في "batch" واحدة، والـ "K/V tensors" بتاعتها بتتحسب وتتخزن في الـ "KV Cache". بعد كده في مرحلة الـ `decode`، النموذج بيولد "token" واحد لكل طلب بشكل متكرر (iteratively)، مع إعادة استخدام الـ "KV Cache" اللي متخزنة وحساب الـ "Query (Q)" tensors. تنفيذ النموذج بيعتمد بشكل كبير على "attention mechanisms" اللي متحسنّة، وبالتحديد "FlashAttention-3"، عشان يعمل عمليات المصفوفات المتوازية على كل الـ "CUDA cores" باستخدام "SIMD(T) operations".
بعد الـ "forward pass"، آخر طبقة في الـ "transformer" بتطلع الـ "logits"، ودي بتمثل الاحتمالات المتوقعة للـ "token" اللي جاي. بعدين الـ vLLM بيطبق استراتيجية "sampling" أو "decoding" (زي الاستراتيجية الـ "greedy" أو "deterministic" أو اللي بتعتمد على الـ "temperature") عشان يختار الـ "token" اللي جاي. الـ "tokens" اللي اتولدت دي بتتبعت من قائمة الإخراج الداخلية بتاعة الـ `EngineCore` تاني للـ `AsyncLLM` عن طريق "IPC"، وهناك بيتعملها "detokenization". في الآخر خالص، الـ `AsyncLLM` بيرجع الإخراج لسيرفر الـ "API"، يا إما مجمّع كاستجابة نهائية واحدة (في وضع "non-streaming")، أو على أجزاء صغيرة عشان التسليم في الوقت الحقيقي (في وضع "streaming"). التركيبة المعقدة دي بتخلي الـ vLLM يحقق أداء ممتاز في خدمة الـ "Large Language Models".
المقال الاصلي: https://www.ubicloud.com/blog/life-of-an-inference-request-vllm-v1