Chunked Prefill
يقسّم التمهيد المسبق المجزأ المطالبات الطويلة إلى أجزاء من الرموز، تتداخل مع فك ترميز الطلبات الأخرى، فلا تعوق مطالبة طويلة واحدة بقية قائمة الانتظار.
الملء المسبق المجزّأ أسلوب في تقديم النماذج يقسّم موجه إدخال طويلًا إلى مجموعات أصغر من الرموز، ويعالجها على عدة خطوات، مع مزجها بـتوليد الرموز للطلبات الأخرى. ومن دون هذا الأسلوب، يقضي محرك الاستدلال الذي يقبل موجهًا طويلًا خطوة كاملة في معالجته، وتتوقف كل استجابة تُبث للمستخدمين الآخرين إلى أن ينتهي. وقدّمت ورقة SARATHI لعام 2023 الملء المسبق المجزّأ، ثم بنى مشروع Sarathi-Serve اللاحق جدولة «خالية من التوقفات» عليه. ويستخدم Dynamic SplitFuse في DeepSpeed-FastGen الفكرة نفسها، وأصبح الملء المسبق المجزّأ مفعّلًا افتراضيًا في vLLM ومتاحًا في SGLang وTensorRT-LLM، وهما محركان يقدّمان كثيرًا من النماذج اللغوية الكبيرة (LLMs) ذات الأوزان المفتوحة.
الملء المسبق وفك الترميز وتوقفات التوليد#
يتألف استدلال المحوّل ذاتي الانحدار من مرحلتين:
- الملء المسبق: يعالج النموذج جميع رموز الموجه بالتوازي، ويحسب مفاتيحها وقيمها، ويخزنها في ذاكرة KV المؤقتة. وينفّذ الملء المسبق عمليات كثيرة مقابل كل بايت من الأوزان المقروءة، لذا يكون عادةً محدودًا بالحوسبة.
- فك الترميز: يولّد النموذج رمزًا واحدًا لكل طلب في كل خطوة، ويقرأ الذاكرة المؤقتة كاملةً في كل مرة. وينفّذ فك الترميز عمليات حسابية قليلة مقابل كل بايت يُقرأ، لذا يكون عادةً محدودًا بعرض نطاق الذاكرة.
تستغرق خطوة GPU تتضمن ملئًا مسبقًا لـ20,000 رمز وقتًا أطول بكثير من خطوة فك الترميز، لذا تنتظرها كل الطلبات في الدفعة. ويسمي Sarathi-Serve هذه التوقفات بتوقفات التوليد، وتظهر لدى المستخدمين الذين تكون موجهاتهم قصيرة على هيئة ارتفاعات في زمن الاستجابة بين الرموز.
كيفية عمل التهيئة المسبقة المجزأة#
تحدد أداة الجدولة ميزانية رموز لكل خطوة، وتُضبط في vLLM باستخدام max_num_batched_tokens. وتضيف أولًا رمزًا واحدًا لكل عملية فك ترميز جارية، ثم تملأ الميزانية المتبقية بقطع من الموجهات المنتظرة. ويتابع الموجه الذي لا تتسع له الخطوة في الخطوة التالية، مع توسيع ذاكرة KV المؤقتة قطعةً بعد قطعة حتى يُعالج الموجه بأكمله ويبدأ فك ترميزه. كما يحسّن مزج المرحلتين في خطوة واحدة استغلال GPU، لأن عمليات فك الترميز تترك جزءًا من قدرة الحوسبة خاملاً تستطيع قطع الملء المسبق استغلاله:
لا يغيّر التقسيم سوى توقيت معالجة رموز الموجه. وتنتبه كل قطعة إلى المفاتيح والقيم المخزنة مؤقتًا لكل القطع السابقة، وإلى رموزها نفسها، ضمن قناع سببي؛ لذلك تعادل النتيجة الملء المسبق في مرور واحد، كما توضح طبقة انتباه واحدة في PyTorch:
import torch
import torch.nn.functional as F
torch.manual_seed(0)
prompt_len, chunk = 1000, 256
q, k, v = torch.randn(3, 1, 8, prompt_len, 64).unbind(0) # one layer: 8 heads, 64 dims each
# One-shot prefill: all prompt tokens attend causally in a single pass
full = F.scaled_dot_product_attention(q, k, v, is_causal=True)
# Chunked prefill: each chunk attends to the cached keys and values of earlier chunks plus itself
outputs = []
for start in range(0, prompt_len, chunk):
end = min(start + chunk, prompt_len)
mask = torch.ones(end - start, end, dtype=torch.bool).tril(diagonal=start) # causal over absolute positions
q_chunk, k_cached, v_cached = q[..., start:end, :], k[..., :end, :], v[..., :end, :]
outputs.append(F.scaled_dot_product_attention(q_chunk, k_cached, v_cached, attn_mask=mask))
chunked = torch.cat(outputs, dim=2)
print(f"{len(outputs)} chunks, same result as one pass: {torch.allclose(full, chunked, atol=1e-5)}")يطبع البرنامج 4 chunks, same result as one pass: True: إذ يُعالج الموجه المؤلف من 1,000 رمز في قطع تضم 256 و256 و256 و232 رمزًا، وتتطابق المخرجات مع معالجة المرور الواحد.
تقنيات التقديم ذات الصلة#
- التجميع المستمر: يضيف الطلبات ويزيلها في كل خطوة بدلًا من انتظار انتهاء دفعة كاملة. ويجعل الملء المسبق المجزّأ جدولة الموجهات الطويلة ممكنة داخل هذه الحلقة.
- PagedAttention: يخزّن ذاكرة KV المؤقتة في صفحات ثابتة الحجم، بحيث يستطيع موجه يُملأ مسبقًا عبر خطوات متعددة توسيع ذاكرته المؤقتة من دون حجز مساحة لطوله الكامل مقدمًا.
- تخزين الموجهات مؤقتًا: يعيد استخدام المفاتيح والقيم المخزنة مؤقتًا لبادئة موجه متكررة، ما يلغي الملء المسبق لذلك الجزء بالكامل.
- فصل الملء المسبق عن فك الترميز: يشغّل المرحلتين على مجموعتين منفصلتين من وحدات GPU، وينقل ذاكرة KV المؤقتة بينهما، كي يمكن توسيع كل مجموعة وضبطها وفق عنق الزجاجة الخاص بها.
- فك الترميز التخميني: يسرّع مرحلة فك الترميز عبر صياغة عدة رموز والتحقق منها في مرور واحد.
التطبيقات#
يكتسب الملء المسبق المجزّأ أهميته عند مشاركة الموجهات الطويلة والقصيرة خادمًا واحدًا. فبوسع مساعد التوليد المعزز بالاسترجاع (RAG) الذي يملأ نافذة السياق بتقرير من 50 صفحة أن يعالجه مسبقًا على شكل قطع بينما يستمر بث محادثات المستخدمين الآخرين. وتضيف النماذج البصرية اللغوية (VLMs) مئات أو آلاف رموز الصور إلى كل موجه، لذا تنتج مسارات الكاميرا التي ترسل الإطارات لوصفها هذا المزيج تحديدًا. وترسل واجهة Ultralytics للنماذج اللغوية الكبيرة النصوص والصور إلى أي نقطة نهاية متوافقة مع OpenAI، بما فيها خادم vLLM مستضاف ذاتيًا، حيث تُملأ موجهات مسار YOLO26 لوصف الإطارات التي يكتشف فيها أشخاصًا مسبقًا على شكل قطع بالتوازي مع حركة المستخدمين الآخرين.
ضبط ميزانية الرموز#
تنطوي الميزانية على مفاضلة بين مقياسي زمن الاستجابة. فميزانية صغيرة تترك مجالًا ضيقًا للملء المسبق في كل خطوة، ما يخفض زمن الاستجابة بين الرموز للمستخدمين الذين يتلقون البث ويؤخر ظهور الرمز الأول للموجهات الجديدة؛ أما الميزانية الكبيرة فتؤدي إلى العكس. كما تعيد كل قطعة لاحقة من الموجه الطويل قراءة ذاكرة KV المتنامية للقطع السابقة، لذا تزيد القطع الصغيرة جدًا حركة الذاكرة. ويضبط المشغّلون الميزانية بالاستناد إلى زمن استجابة الاستدلال الوسيط والطرفي، والزمن حتى الرمز الأول، ومعدل الإنتاجية عند استخدام أطوال موجهات واقعية. ويذكر دليل تحسين vLLM أن ميزانية 2,048 رمزًا تفضّل زمن الاستجابة بين الرموز، ويوصي بأكثر من 8,192 رمزًا لبلوغ أقصى إنتاجية مع النماذج الصغيرة على وحدات GPU كبيرة.










