يعد تقديم LLMs للعديد من التطبيقات والمستخدمين بالتوازي أمرًا صعبًا لأنهم يتنافسون على موارد GPU المحدودة. هذه المقالة هي الأولى في سلسلة حول أداء LLM، استنادًا إلى خبرتنا في خدمة LLMs المستضافة ذاتيًا في TNG Technology Consulting GmbH. في الجزء الأول، نركز على تأثير قوائم الانتظار ونناقش استراتيجيات الجدولة المختلفة.

نقطة البداية: محرك الاستدلال المجرد

يتكون محرك الاستدلال مثل vLLM أو HuggingFace TGI من

  • عامل يقوم بالعمل الفعلي لحساب الرمز المميز التالي في الطلب
  • قائمة انتظار تضاف إليها الطلبات عند وصولها لأول مرة
  • مجدول يأخذ الطلبات من قائمة الانتظار وينقلها إلى العامل

لماذا نحتاج إلى قائمة الانتظار هنا؟ لأن العمليات الحسابية على وحدة معالجة الرسومات تكون أكثر أداءً وأكثر كفاءة في استخدام الموارد عند إجرائها على دفعات بدلاً من عزلها للطلبات الفردية. تسمح قائمة الانتظار الخلفية هذه للمجدول باختيار طلبات متعددة ووضعها على نفس الدفعة المراد معالجتها.

لاحظ أن كل محرك استدلال يخدم عادةً نموذجًا واحدًا فقط، ولدينا عمليات نشر متعددة تعمل لنماذج مختلفة بالتوازي.

المشكلة: يمكن “للمستخدمين المتميزين” حظر المستخدمين الآخرين

عندما يرسل مستخدم واحد “أ” عددًا كبيرًا من الطلبات، فسوف يملأ قائمة الانتظار بسرعة. يتم منع المستخدمين الآخرين (“B” و”C”) الذين يرسلون طلباتهم بعد فترة وجيزة من استخدام نفس النموذج (حتى تتم معالجة جميع الطلبات الواردة من “A”). لاحظ أن الصورة تركز على vLLM كمحرك استدلال، لكن المشكلة أكثر عمومية وتنطبق على الواجهات الخلفية الأخرى أيضًا.

صورة / بابوا نيو غينيا

الحل: جدولة عادلة

في TNG، لا يرسل المستخدمون الطلبات مباشرة إلى الواجهة الخلفية لـ vLLM ولكن إلى خادم API (ما نسميه “LLM-Server”). هنا يمكن أن يكون لدينا قوائم انتظار منفصلة لكل مستخدم (ونموذج)، ومجدول ليس FIFO (الوارد أولاً – يخرج أولاً) ولكنه يدور حول جميع قوائم انتظار المستخدم. وهذا يحقق بعض “جدولة عادلة”: على سبيل المثال، في الرسم التخطيطي، يرسل المستخدمون B وC طلبهم في وقت لاحق قليلاً، عندما تتم جدولة الطلب الأول للمستخدم A بالفعل. في ذلك الوقت، كانت هناك ثلاثة طلبات من المستخدم “أ” تنتظر بالفعل لبعض الوقت، ولكن يتعين على المستخدم “ج” الانتظار حتى يكتمل طلب واحد فقط من المستخدم “أ”.

الفكرة الأساسية هي: تحديد أولويات الطلبات الواردة من مستخدمين مختلفين في المكون الخاص بنا، وليس في الواجهة الخلفية للاستدلال! عادةً، لا يمكنك تغيير ترتيب الطلبات بمجرد إرسالها إلى محرك الاستدلال، لذلك يتعين عليك إحضارها بالترتيب الصحيح بينما لا تزال في LLM-Server، حيث لدينا السيطرة الكاملة.

صورة / بابوا نيو غينيا

الامتدادات المحتملة

يمكنك النظر في جوانب مختلفة من أجل تحديد الجدولة “العادلة”. في المثال أعلاه، يجب تقديم الخدمة لأي مستخدم جديد لديه طلب واحد قبل تقديم الخدمة لأي مستخدم مرتين أو أكثر على التوالي. وهذا أمر “عادل” على مستوى عدد الطلبات. ولكن يمكنك أيضًا أن تنظر إلى وقت المعالجة: ما المدة التي يبقي فيها الطلب الواجهة الخلفية مشغولة؟ المطالبات الطويلة والأجيال الطويلة سوف “تمنع” LLM للمستخدمين الآخرين لفترة أطول، لذلك ربما يجب أن يكون للطلبات الأقصر الأسبقية؟ لسوء الحظ، من الصعب جدًا تقدير طول الجيل. في حين أن هناك بعض الطلبات بحد “max_tokens”، فإن رسالة الدردشة النموذجية من مساعد الذكاء الاصطناعي التفاعلي ليس لها حد للرمز المميز، ويمكن أن تختلف بين جيل قصير جدًا (“تلخيص هذا النص”) وجيل طويل جدًا (“أخبرني قصة”https://huggingface.co/”اكتب كل التعليمات البرمجية لـ xyz”).

اعتمادًا على المطالبات، يمكن أن تكون هناك بعض الفوائد في ترتيب الطلبات بناءً على التشابه، بحيث يمكن لـ vLLM تعظيم عدد زيارات ذاكرة التخزين المؤقت، مما يسرع الأداء. لقد اكتسب هذا النوع من التوجيه المدرك لذاكرة التخزين المؤقت لـ KV اهتمامًا مؤخرًا من قبل أطر عمل مثل NVIDIA Dynamo وAIBrix.

في سياق الأعمال التجارية، وبالنسبة لـ LLMs المستضافة، فإن يكلف يمكن أن يكون عدد الطلبات الفردية مقياسًا آخر يجب أخذه في الاعتبار، ولكنه يأتي مع تحديات مماثلة.

يمكن أيضًا توسيع الحل من خلال عدم وجود قائمة انتظار واحدة فقط لكل مستخدم (وطراز) ولكن أيضًا عدة طوابير بأولويات مختلفة. على سبيل المثال، يجب أن تتمتع التطبيقات التفاعلية مثل TNG’s AI Assistant مع واجهة الدردشة بأولوية أعلى لأن المستخدمين الذين لا يرون أي تقدم خلال خمس ثوانٍ سيعتقدون أن التطبيق معطل. ومع ذلك، فإن المستخدمين الذين يقومون بإنشاء مراجعات التعليمات البرمجية لعشرات الملفات وعدة آلاف من أسطر التعليمات البرمجية، يتوقعون أن تستغرق طلبات LLM بعض الوقت. وبعض حالات الاستخدام (مثل عمليات التشغيل المعيارية، المجدولة عبر واجهة برمجة التطبيقات المجمعة) يجب أن تكون لها أولوية منخفضة بحيث لا تزعج حالات الاستخدام الأخرى ويتم جدولتها فقط عندما لا يتم تشغيل أي شيء آخر.

المشكلة: لا يوجد ضغط خلفي من خلال قائمة انتظار الواجهة الخلفية

خذ بعين الاعتبار مرة أخرى السيناريو الذي يرسل فيه مستخدم واحد “أ” الكثير من الطلبات في وقت واحد، وبعد مرور بعض الوقت ينضم مستخدم جديد “ج” ويريد جدولة طلب واحد. إذا أرسل المجدول من جانب LLM-Server كل طلب (وفقًا للأولوية العادلة) فورًا إلى الواجهة الخلفية، فستتراكم جميع الطلبات الواردة من A مرة أخرى في قائمة انتظار FIFO. سيتعين على المستخدم الجديد C الانتظار مرة أخرى حتى تتم معالجة جميع الطلبات المستلمة مسبقًا. لن يكون هناك أي تحسن تقريبًا في السيناريو الأولي بدون LLM-Server.

من الناحية المثالية، يمكنك تحديد الحد الأقصى لعدد العناصر في قائمة انتظار الواجهة الخلفية، ولكن vLLM لا يمنحك هذا الخيار. لذلك، يتعين علينا ضبط المعدل ديناميكيًا الذي يرسل به المجدول من جانب LLM-Server طلبات جديدة إلى الواجهة الخلفية. وهنا هدفنا هو: اجعل طول قائمة انتظار الواجهة الخلفية قصيرًا من أجل تقليل فترات الاستجابة التي يواجهها المستخدمون الجدد.

(أبسط نهج هو وضع حد للمعدل الثابت، ولكن من المحتمل أن يؤدي هذا إلى عدم الاستخدام الكافي عندما تكون معظم الطلبات قصيرة ويصعب معايرتها لنماذج وأنماط تحميل مختلفة).

الحل: جلب المقاييس

من أجل إتاحة طول قائمة انتظار الواجهة الخلفية في LLM-Server، نحتاج إلى جلب مقاييس Prometheus ذات الصلة من نقطة نهاية vLLM /metrics. يُسمح لمجدولنا العادل فقط بإرسال الطلبات إلى الواجهة الخلفية طالما أن متري طول قائمة الانتظار الخلفية أصغر من ثلاثة، على سبيل المثال. يمكن خفض هذا الطول المستهدف لقائمة الانتظار الخلفية للحصول على زمن استجابة أقصر للمستخدمين الجدد، حتى يؤدي ذلك إلى دفعات غير مستغلة بشكل كافٍ وانخفاض الكفاءة – هناك مقايضة. ومع ذلك، ضع في اعتبارك أن طول قائمة الانتظار المستهدفة لا يعكس الحد الأقصى للتزامن؛ من الممكن أن يكون هناك أكثر من ثلاثة طلبات قيد المعالجة بالتوازي؛ سيضيف vLLM طلبات الانتظار إلى الدفعة الحالية بمجرد توفر مساحة كافية (“التجميع المستمر”).

صورة / بابوا نيو غينيا

الامتدادات المحتملة

بمجرد حصولك على حلقة الملاحظات بين قائمة الانتظار الخلفية والمجدول في LLM-Server، يمكنك بسهولة توسيع مجموعة مقاييس vLLM المستخدمة لاتخاذ قرارات الجدولة. على سبيل المثال، للحصول على تجربة مستخدم جيدة في مساعد الذكاء الاصطناعي التفاعلي الخاص بـ TNG، نهدف إلى سرعة عالية في إنشاء الرموز المميزة (على سبيل المثال، > 7 رموز/ثانيات، أي حوالي 150 مللي ثانية لكل رمز مميز)، وإذا تم الإبلاغ عنها مقياس الوقت لكل رمز مميز للمخرجات يرتفع فوق 150 مللي ثانية، ولن تتم جدولة أي طلبات جديدة.

يمكنك أيضًا برمجة عتبات مترية مختلفة لأولويات الطلب المختلفة. على سبيل المثال، بالنسبة للطلبات ذات الأولوية المنخفضة من واجهة برمجة التطبيقات المجمعة، فإننا نقوم فقط بجدولة الطلب عندما تكون قائمة انتظار الواجهة الخلفية فارغة تمامًا: نفضل المخاطرة بفترات قصيرة من وحدات معالجة الرسومات غير المستغلة بشكل كافٍ بدلاً من التسبب في زيادة فترات الاستجابة لأي طلب لاحق ذي أولوية أعلى.

هناك بعض الإمكانيات للتحسينات: على سبيل المثال، إذا سمحت بالجدولة لطول الواجهة الخلفية أقل من ثلاثة وكان المقياس الحالي صفرًا، فيمكنك إرسال ثلاثة طلبات على الفور إلى الواجهة الخلفية قبل الاضطرار إلى جلب المقاييس مرة أخرى. في أسوأ الحالات، لا يتناسب أي منها مع الدفعة الحالية وكلها موجودة في قائمة انتظار الواجهة الخلفية (وفي هذه الحالة يكون الطول الجديد ثلاثة، فلا بأس بذلك).

البديل: جدولة أولويات الواجهة الخلفية

في الآونة الأخيرة، أضاف vLLM خيار الجدولة على أساس الأولوية بدلاً من الالتزام بـ FIFO. بالنسبة لهذه الميزة الجديدة، يتم وضع علامة على الطلبات ذات الأولوية قبل إرسالها إلى الواجهة الخلفية. وسيقوم vLLM بالتحقق بانتظام مما إذا كان هناك أي طلب ذي أولوية أعلى في قائمة الانتظار ثم يقوم بفرز جميع الطلبات حسب الأولوية (كل من الدفعة الجاري تشغيلها وقائمة الانتظار المنتظرة). لن يؤدي هذا إلى السماح للطلب ذي الأولوية العالية بالقفز فعليًا إلى قائمة الانتظار فحسب، بل سينقله أيضًا مباشرة إلى الدفعة التي تمت معالجتها. والسعر (بصرف النظر عن بعض النفقات العامة بسبب الفرز) هو أنه سيتم طرد الطلبات ذات الأولوية المنخفضة من الدفعة قيد التشغيل وإعادتها إلى قائمة انتظار الانتظار مرة أخرى.

صورة / بابوا نيو غينيا

هل يمكن لجدولة أولويات الواجهة الخلفية استبدال جميع قوائم الانتظار على جانب خادم LLM؟

يفهم vLLM أولوية الطلب كرقم مستمر. لا يسمح لك هذا فقط بالتمييز بين الأولوية الافتراضية والعالية (مساعد الذكاء الاصطناعي التفاعلي) والأولوية المنخفضة (واجهة برمجة التطبيقات المجمعة) ولكن يمكنك أيضًا إجراء تخفيضات أصغر لكل طلب أرسله المستخدم بالفعل إلى الواجهة الخلفية وحيث تكون الاستجابة معلقة. على سبيل المثال، يرسل المستخدمون B وC طلبات فردية فقط، والتي ستكون الأولوية لها صفر. يرسل المستخدم أ أربعة طلبات مرة واحدة؛ الأول له الأولوية صفر أيضًا، ولكن الأولويات التالية ستكون لها الأولوية الأولى والثانية والثالثة، وستتم معالجتها لاحقًا (الرقم الأعلى = الأولوية الأقل).

لا تزال هناك بعض التحذيرات:

  • ميزة أولوية الواجهة الخلفية متاحة فقط لـ vLLM، وليس لـ HuggingFace TGI.
  • يتيح لك وجود برنامج جدولة على جانب LLM-Server ضبط معدل الجدولة بناءً على الوقت لكل رمز إخراج. لا تتحكم جدولة أولوية الواجهة الخلفية في معدل الجدولة.
  • لا يزال هناك حاجة إلى قياس تأثير إعادة الترتيب المتكرر لقائمة الانتظار والدُفعة على فترات الاستجابة من أجل سيناريوهات التحميل الواقعية.

بشكل عام، يمكن أن تكون جدولة أولويات الجانب الخلفي استراتيجية جيدة للأنظمة المستندة إلى vLLM، لأنها تبسط منطق الانتظار على خادم LLM-Server. لسوء الحظ، في بيئة واقعية، لا يمكنك التخلص من LLM-Server كطبقة جدولة إضافية، نظرًا لأنك تحتاج إلى بعض الأمثلة الموضوعية لتعيين الأولويات للطلبات الفردية.

ملخص والتوقعات

تعد قائمة الانتظار والجدولة أمرًا بالغ الأهمية للتطبيقات التي تضم العديد من المستخدمين والعملاء ذوي الأولويات المختلفة، لأنها تؤثر بشكل كبير على تجربة المستخدم. وينطبق هذا بشكل خاص في السيناريوهات التي يقوم فيها العديد من العملاء بتقديم طلبات بالتوازي، مستفيدين من استراتيجية “الجدولة العادلة” المخصصة. على الرغم من أن ميزات الواجهة الخلفية مثل جدولة الأولويات يمكنها تبسيط عملية التحسين، إلا أن خادم البوابة الأولية لا يزال ضروريًا لإدارة هذه التعقيدات بشكل كامل.

في الجزء التالي من سلسلة منشورات المدونات هذه، سنغير المسار ونركز على إنشاء الرمز المميز في محرك الاستدلال أثناء مرحلتي التعبئة المسبقة وفك التشفير. وعلى وجه الخصوص، سنناقش استخدام الموارد واستراتيجيات المعالجة المتزامنة للطلبات المتعددة.

شاركها.
اترك تعليقاً