self-hosted LLM 5 صارفین پر سست کیوں ہو جاتا ہے؟
Ollama کا default parallel requests limit 1 ہے۔ جانیں batching، KV cache، prefill اور queue depth کیسے طے کرتے ہیں کہ LLM server بیک وقت کتنے صارفین سنبھال سکتا ہے۔
جب مزید صارفین آتے ہیں تو self-hosted LLM سست کیوں ہو جاتا ہے؟
self-hosted LLM 5 بیک وقت صارفین پر رک جاتا ہے، کیونکہ server اب بھی ایک وقت میں صرف ایک جواب تیار کر رہا ہوتا ہے اور باقی چار درخواستیں queue میں کھڑی ہوتی ہیں۔ Ollama کی documentation default کے بارے میں واضح ہے: OLLAMA_NUM_PARALLEL یعنی "ہر model ایک ہی وقت میں جتنی parallel requests process کرے گا، ان کی زیادہ سے زیادہ تعداد؛ default 1 ہے۔" کچھ خراب نہیں ہوا۔ آپ کے 5 میں سے 4 صارفین اپنی باری کا انتظار کر رہے ہیں۔
اس کا حل عموماً بڑا server نہیں ہوتا۔ حل ایسا serving engine ہے جو ایک ہی forward pass میں model کے ذریعے متعدد requests process کرے، اور اتنی اضافی memory بھی موجود ہو جو اس دوران تمام صارفین کی conversations محفوظ رکھ سکے۔ دونوں حصے اہم ہیں، لیکن اصل حد دوسرا حصہ مقرر کرتا ہے۔
ہر درخواست جن دو مراحل سے گزرتی ہے
Prefill پورے prompt کو ایک ہی بار پڑھ کر اس کے لیے attention cache بناتا ہے۔ Prompt کے تمام tokens ایک ساتھ model سے گزرتے ہیں، اس لیے prefill ایک بڑی matrix multiply ہوتی ہے اور اس کی حد arithmetic throughput ہوتی ہے۔ اس کے بعد decode جواب کو ایک وقت میں ایک token لکھتا ہے۔ ہر token کے لیے model کے تمام weights دوبارہ memory سے پڑھنے پڑتے ہیں، جبکہ اس ایک token پر ہونے والی arithmetic بہت کم ہوتی ہے۔ Decode کی حد memory bandwidth ہوتی ہے۔
یہی عدم توازن batching کے مؤثر ہونے کی بنیادی وجہ ہے۔ ایک user کے لیے decoding میں فرض کریں کہ ہر token پر weights کے 5 GB پڑھے جاتے ہیں، جبکہ زیادہ تر arithmetic units idle رہتی ہیں۔ دوسری request شامل کریں تو engine وہی 5 GB ایک بار پڑھتا ہے اور پھر ان سے 2 tokens compute کرتا ہے۔ دوسرے user کی اضافی لاگت تقریباً نہ ہونے کے برابر ہوتی ہے۔ Requests کو سختی سے ایک کے بعد ایک serve کرنے سے یہ فائدہ ضائع ہو جاتا ہے۔
دو numbers یہ بیان کرتے ہیں کہ user کو کیا محسوس ہوتا ہے۔ TTFT (time to first token) queue wait اور prefill کا مجموعہ ہے۔ ITL (inter-token latency) streamed tokens کے درمیان وقفہ ہے، اور اس کا تعین decode کرتا ہے۔ Slow server عموماً ان دونوں میں سے کسی ایک وجہ سے slow ہوتا ہے، اور دونوں کے حل مختلف ہوتے ہیں۔ Setting تبدیل کرنے سے پہلے یہ معلوم کرنا ضروری ہے کہ مسئلہ کس مرحلے میں ہے، اور prefill اور decode کی timing الگ الگ ماپنا اسی طرح معلوم ہوتا ہے۔
Static batching سب کو سب سے سست جواب کا انتظار کراتی ہے
Static batching اس کا سادہ طریقہ ہے، اور جب آپ application code میں خود requests کو گروپ کرتے ہیں تو یہی طریقہ استعمال ہوتا ہے۔ Engine N requests جمع کرتا ہے، انہیں ایک ساتھ چلاتا ہے، اور ہر slot کو اس وقت تک مصروف رکھتا ہے جب تک گروپ میں سب سے طویل generation مکمل نہ ہو جائے۔
ایک user کی طرف سے 1,200 tokens کے summary کی request batch میں موجود چار ایک سطری جوابات کو بھی روکے رکھتی ہے، کیونکہ batch اس وقت تک کوئی slot خالی نہیں کرتی جب تک اس کا سب سے سست رکن مکمل نہ ہو جائے۔
اس کے دو نقصانات ہوتے ہیں۔ مکمل ہو چکی sequences ایسے slots پر قابض رہتی ہیں جو مزید کوئی مفید compute نہیں کر رہے ہوتے، اس لیے output lengths مختلف ہونے پر مؤثر throughput کم ہو جاتی ہے، اور chat میں output lengths میں کافی زیادہ فرق ہوتا ہے۔ Batch بننے کے ایک step بعد آنے والی request کو شروع ہونے سے پہلے پورے batch کے ختم ہونے کا انتظار کرنا پڑتا ہے، یعنی اس کا TTFT کسی دوسرے user کی طویل تحریر سے متعین ہوتا ہے۔
مسلسل batching ہر token پر requests کو شامل اور خارج کرتا ہے
مسلسل batching ایک decoding step کی سطح پر scheduling کرتا ہے۔ ہر step کے بعد scheduler ان sequences کو خارج کر دیتا ہے جنہوں نے ابھی اپنا stop token جاری کیا ہو، پھر انتظار کرنے والی requests کو خالی slots میں شامل کرتا ہے۔ جو reply step 40 پر ختم ہو جائے، اس کا slot step 40 پر ہی خالی ہو جاتا ہے، batch کے اختتام پر نہیں۔
یہ کوئی غیر معمولی طریقہ نہیں ہے۔ llama-server میں -cb, --cont-batching کو یوں document کیا گیا ہے: "whether to enable continuous batching (a.k.a dynamic batching) (default: enabled)"، اور vLLM اسی تصور کے گرد بنایا گیا ہے۔ Ollama بھی parallel requests فراہم کرتا ہے۔ Default صرف تعداد کو 1 تک محدود کرتا ہے۔ اسی لیے بہت سے لوگ یہ نتیجہ اخذ کرتے ہیں کہ ان کا hardware concurrency نہیں چلا سکتا، حالانکہ ان کی configuration نے اسے منع کیا ہوتا ہے۔
شائع شدہ continuous batching کے نتائج عموماً datacenter cards پر ناپے جاتے ہیں، جن میں اضافی compute اور cache کے لیے دسیوں gigabytes دونوں موجود ہوتے ہیں۔ ان نتائج کی عمومی ساخت آپ کے box پر بھی لاگو ہوتی ہے۔ ان کے اعداد و شمار لاگو نہیں ہوتے، اور اس کی وجہ نیچے دیا گیا memory section بیان کرتا ہے۔
Prefill اسی compute وسائل کے لیے decode سے مسابقت کرتا ہے
جب چار جوابات stream ہو رہے ہوں اور اسی دوران نئی request آئے، تو پہلے اس کے prompt کا prefill کرنا پڑتا ہے، جبکہ prefill compute کے لحاظ سے بھاری عمل ہے۔ اگر scheduler اس prefill کے لیے الگ step مختص کرے، تو اس دوران stream دیکھنے والے چاروں صارفین کو کوئی token موصول نہیں ہوگا۔ طویل prompt کی صورت میں ہر کھلی window میں یہ وقفہ واضح دکھائی دیتا ہے۔ جب لوگ کہتے ہیں کہ کسی دوسرے شخص کے send دبانے پر server ہچکولے کھاتا ہے، تو ان کی مراد یہی stutter ہوتی ہے۔
Chunked prefill طویل prompt کو حصوں میں تقسیم کرتا ہے اور ہر حصے کو جاری decodes کے اسی step میں شامل کرتا ہے۔ vLLM کی tuning guide اس tradeoff کو براہ راست بیان کرتی ہے: چھوٹے chunk budgets سے "achieve better ITL because there are fewer prefills slowing down decodes"، جبکہ زیادہ values سے "achieve better time to first token (TTFT) as you can process more prefill tokens in a batch"۔ آپ کو یہ طے کرنا ہوتا ہے کہ کس کے تجربے کو ترجیح دینی ہے: جواب شروع ہونے کے منتظر شخص کے تجربے کو، یا stream ہوتے متن کو دیکھنے والے لوگوں کے تجربے کو۔
Prompt کی لمبائی اس اثر کی شدت طے کرتی ہے۔ 6,000 token کا prompt اور 200 token کا جواب، 200 decode steps کے مقابلے میں prefill کے 6,000 tokens کا کام پیدا کرتے ہیں۔ Retrieval-augmented chat اور طویل system prompts دونوں آپ کو اسی صورتِ حال میں لے جاتے ہیں۔ اس لیے prefill معمولی اضافی کام نہیں رہتا بلکہ وہ مرحلہ بن جاتا ہے جس کا صارفین انتظار کرتے ہیں۔ جب طویل حصہ بار بار دہرایا جائے تو Prefix caching مدد دیتی ہے: vLLM --enable-prefix-caching فراہم کرتا ہے، جو ہر request کے لیے shared prompt prefix کو دوبارہ compute کرنے کے بجائے اس کا cache دوبارہ استعمال کرتا ہے۔
پہلے ختم ہونے والی memory KV cache ہوتی ہے
ہر active conversation کا ہر token model کی ہر layer میں ایک key vector اور ایک value vector چھوڑتا ہے۔ اسے KV cache، یعنی key/value cache، کہتے ہیں۔ یہی cache decode کو ہر نئے token کے لیے پورا prompt دوبارہ compute کرنے سے بچاتی ہے۔ ہر token کے لیے اس کا سائز model کی ساخت سے مقرر ہوتا ہے: 2، یعنی ایک key اور ایک value، کو layer count، key/value heads کی تعداد، head dimension، اور ہر value کے لیے bytes سے ضرب دی جاتی ہے۔ یہ اعداد model کے config.json سے حاصل کریں۔
اس حساب کو ایک بار کر لیں تو maximum capacity واضح ہو جاتی ہے۔ ایک عام 8B model میں 36 layers، 8 key/value heads اور 128 کا head dimension ہو، اور cache کو 16-bit میں رکھا جائے، تو ہر token کے لیے لاگت 2 36 8 128 2 bytes ہوتی ہے۔ یہ 147,456 bytes، یعنی تقریباً 144 KiB، بنتے ہیں۔ 8,192 tokens کی ایک conversation کے لیے تقریباً 1.2 GB cache درکار ہوتی ہے۔ ایسی پانچ conversations کے لیے تقریباً 6 GB درکار ہوں گی، weights کے علاوہ۔ صارفین کی گنجائش کا حقیقی جواب یہی ہے۔
Concurrency context کو کئی گنا بڑھا دیتی ہے، اور tools یہ بات واضح طور پر بتاتے ہیں۔ Ollama کے FAQ میں لکھا ہے: "کسی model کے لیے parallel request processing، parallel requests کی تعداد کے مطابق context size بڑھا دیتی ہے۔ مثال کے طور پر، 4 parallel requests کے ساتھ 2K context کا نتیجہ 8K context اور اضافی memory allocation ہوتا ہے۔" مطلوبہ RAM، OLLAMA_NUM_PARALLEL کو OLLAMA_CONTEXT_LENGTH سے ضرب دینے کے مطابق بڑھتی ہے۔ llama-server میں -c کے ذریعے مانگا گیا context، -np slots میں تقسیم ہوتا ہے۔ اس لیے صرف slots کی تعداد بڑھانے سے ہر request کے لیے دستیاب گنجائش کم ہو جاتی ہے۔ Per-slot context کا اندازہ خود لگانے کے بجائے اسے startup log سے پڑھیں۔
vLLM پہلے سے memory allocate کرتا ہے۔ --gpu-memory-utilization، جس کی default value 0.92 ہے، "model executor کے لیے استعمال ہونے والی GPU memory کا حصہ" ہے۔ Weights کے بعد جو memory بچتی ہے، وہ paged KV pool بن جاتی ہے۔ جب یہ pool کم پڑ جائے تو scheduler request کو fail کرنے کے بجائے اسے evict کر دیتا ہے:
WARNING 05-09 00:49:33 scheduler.py:1057] Sequence group 0 is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space.vLLM کے V1 engine میں preemption کا default mode RECOMPUTE ہے۔ اس لیے evict کی گئی request اپنی cache کھو دیتی ہے اور دوبارہ admit ہونے پر دوبارہ prefill ہوتی ہے۔ یہ کام دو بار ہوتا ہے۔ Documentation خبردار کرتی ہے کہ "preemption اور recomputation end-to-end latency کو بری طرح متاثر کر سکتے ہیں"۔ یہ log line اس بات کی بہترین وضاحت ہے کہ average latency معمول کے مطابق ہونے کے باوجود ایک صارف کو باقی سب سے کہیں زیادہ انتظار کیوں کرنا پڑا۔ cumulative count log کرنے کے لیے disable_log_stats=False set کریں، یا vLLM کے فراہم کردہ Prometheus metrics سے preemption counter پڑھیں۔
2، 5 اور 20 بیک وقت صارفین پر کیا تبدیلی آتی ہے
دو صارفین۔ اضافی cache والے GPU پر اثر تقریباً نظر نہیں آتا، کیونکہ دوسری decode stream پہلی stream کے ساتھ چلتی ہے اور اضافی وقت بہت کم لیتی ہے۔ صرف CPU والے 4 سے 8 GB RAM کے VPS پر یہ مفت نہیں ہوتا: دونوں streams وہی چند vCPUs اور RAM bandwidth استعمال کرتی ہیں، اس لیے ہر صارف کو تقریباً نصف tokens per second ملتے ہیں، جبکہ cache کی ضرورت دوگنی ہو جاتی ہے اور دستیاب budget بہت کم ہوتا ہے۔
پانچ صارفین۔ یہاں defaults کافی نہیں رہتے، اور مسئلہ queue سے شروع ہوتا ہے۔ OLLAMA_NUM_PARALLEL کو 1 پر رکھنے کی صورت میں چار افراد اس شخص کا انتظار کرتے ہیں جس نے طویل جواب طلب کیا ہو، اور اپنی باری آنے پر ہر شخص کو معمول کی رفتار ملتی ہے۔ parallel count بڑھانے سے مسئلہ تبدیل ہو جاتا ہے: ہر slot کے لیے 8K context رکھنے پر 40K tokens کے cache کی ضرورت ہوتی ہے۔ اگر یہ VRAM میں نہ سما سکے تو engine layers کو system RAM میں offload کرتا ہے، اور اگر یہ RAM میں بھی نہ سما سکے تو box swap استعمال کرنے لگتا ہے اور tokens per second تیزی سے کم ہو جاتے ہیں۔
بیس صارفین۔ chat UI میں بیس انسان عموماً بیس بیک وقت requests نہیں بھیج رہے ہوتے۔ hardware خریدنے سے پہلے سمجھنے کے لیے یہ سب سے اہم بات ہے۔ ایک شخص جواب پڑھتا ہے اور اگلی باری سے پہلے 20 سے 60 سیکنڈ تک سوچتا ہے، اس لیے اس کے session کا زیادہ تر حصہ idle ہوتا ہے۔ بیس agents یا بیس document summarisation jobs بیس حقیقی streams ہیں، جن میں بالکل idle time نہیں ہوتا۔ یہ مختلف مشین کا تقاضا ہے۔ ایک developer جو coding agent کو اپنے Ollama server سے منسلک کرتا ہے پہلے معاملے کے مقابلے میں دوسرے معاملے کے زیادہ قریب ہوتا ہے، کیونکہ task جاری رہنے تک agent مسلسل requests بھیجتا رہتا ہے اور انسان کے مطالعے کے دوران آنے والے وقفے نہیں رکھتا۔
کیا آپ کے صارفین بیک وقت درخواستیں بھیجتے ہیں، یا صرف لاگ اِن ہوتے ہیں؟
کسی بھی sizing سے پہلے معلوم کریں کہ کتنی requests زیرِ عمل ہوتی ہیں۔ حساب سادہ ہے: زیرِ عمل requests کی تعداد = صارفین کی تعداد × ہر turn کے لیے generation میں صرف ہونے والے سیکنڈ ÷ دو turns کے درمیان سیکنڈ۔
- پہلے اپنی single-stream رفتار کی پیمائش کریں، یعنی prefill اور decode دونوں کی۔ کسی دوسرے شخص کے card کا عدد استعمال نہ کریں: اپنے box پر tokens per second کی پیمائش کریں اور حاصل شدہ قدر استعمال کریں۔
- duty cycle کا اندازہ لگائیں۔ 20 chat users، ہر turn کے لیے 12 seconds کی generation، اور ہر 90 seconds میں 1 turn سے 20 * 12 / 90 بنتا ہے، یعنی تقریباً 2.7 requests زیرِ عمل۔
- slot count اس قدر سے کچھ زیادہ رکھیں، پھر memory کے مقابل اس کی جانچ کریں: slots × فی request context، آپ کے پاس حقیقتاً موجود cache tokens کے اندر آنا چاہیے۔
- queue مختصر رکھیں تاکہ گنجائش سے زیادہ requests فوری اور واضح طور پر fail ہوں۔
دستیاب cache tokens، weights لوڈ ہونے کے بعد بچنے والی free memory کو اوپر والے section میں دی گئی فی token لاگت سے تقسیم کرنے سے حاصل ہوتے ہیں۔ 24 GB card پر 16-bit میں 8B model چلانے کے لیے تقریباً 16 GB weights استعمال ہوتے ہیں، اور default utilisation پر usable cache تقریباً 6 GB رہتی ہے، جو تقریباً پانچ 8K conversations کے لیے کافی ہے۔ مزید requests بیک وقت چلانے کے لیے فی request context مختصر کریں، یا cache کو 8-bit میں store کریں (llama-server takes --cache-type-k q8_0)۔ دونوں طریقے کسی چیز کی قربانی دے کر concurrency بڑھاتے ہیں، اور hardware پر رقم خرچ کرنے سے پہلے اس trade-off کی دیانت دار وضاحت پڑھنا مفید ہے: GPU VPS، API tokens کے مقابل کب لاگت پوری کرتا ہے۔
جب Ollama کی default ترتیبات کافی نہ رہیں
Parallel count کو service unit کے ذریعے بڑھائیں، کیونکہ shell export، systemd کے زیر انتظام daemon تک نہیں پہنچے گا۔
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_CONTEXT_LENGTH=8192"
Environment="OLLAMA_MAX_QUEUE=64"sudo systemctl daemon-reload
sudo systemctl restart ollama
systemctl show ollama --property=Environment
ollama pssystemctl show کو ان تین variables کو دکھانا چاہیے جو آپ نے ابھی set کیے ہیں۔ اگر ایسا نہ ہو تو drop-in save نہیں ہوا، اور اس کے بعد آپ جو بھی کریں گے وہ مؤثر نہیں ہوگا۔ ollama ps اس loaded model کو بھی دکھاتا ہے جس کا size صرف weights سے بڑا ہوتا ہے، کیونکہ 8,192 tokens کے 4 slots، model کے ساتھ 32,768 tokens کی cache reserve کرتے ہیں۔ اگر آپ کو model مکمل طور پر GPU پر رکھنے کی توقع تھی، لیکن PROCESSOR column میں model کا کچھ حصہ CPU پر دکھائی دے، تو اس کا مطلب ہے کہ آپ نے graphics card میں دستیاب cache سے زیادہ cache مانگی ہے۔ دونوں numbers میں سے ایک کو کم کریں۔ عموماً context کم کرنا زیادہ محفوظ طریقہ ہے، لیکن بہت چھوٹی window طویل prompts کو error دینے کے بجائے خاموشی سے truncate کر دیتی ہے۔ اس لیے model کے fit ہونے تک اسے کم کرتے رہنے کے بجائے num_ctx کا حجم سوچ سمجھ کر طے کرنا بہتر ہے۔
Queue کی default ترتیب کا دوبارہ جائزہ لینا چاہیے۔ Ollama زیادہ سے زیادہ OLLAMA_MAX_QUEUE requests کو queue میں رکھتا ہے، اور "default 512 ہے"۔ اس حد سے آگے یہ "503 error کے ساتھ جواب دیتا ہے، جس سے ظاہر ہوتا ہے کہ server overloaded ہے"۔ ایسے box پر، جو ایک وقت میں 4 requests serve کرتا ہو، 512 requests کی گہری queue ایک ایسا وعدہ ہے جسے آپ پورا نہیں کر سکتے، کیونکہ 300ویں position پر موجود client اپنی باری آنے سے بہت پہلے timeout ہو جاتا ہے۔ مختصر queue ایسا error واپس کرتی ہے جسے آپ کی application retry یا report کر سکتی ہے۔ یہ ایسے spinner سے بہتر ہے جو کبھی مکمل نہ ہو۔
حقیقی طور پر test کریں۔ اسی وقت دو terminals سے دو requests بھیجیں اور دونوں کو monitor کریں۔ اگر دوسری request پہلی کے مکمل ہونے تک کوئی output نہ دے، تو parallel setting مؤثر نہیں ہوئی۔
جب ایک حقیقی serving engine اپنے اضافی انتظامی اخراجات پورے کرنے لگے
vLLM کا اضافی setup اس وقت فائدہ مند ہوتا ہے جب آپ کے پاس کچھ گنجائش رکھنے والا GPU ہو اور تقریباً چار سے زیادہ requests واقعی زیرِ عمل ہوں۔ اس کا scheduler ہر token کی سطح پر کام کرتا ہے، اس کا cache pages میں تقسیم ہوتا ہے تاکہ خالی fragments دوبارہ استعمال ہو سکیں، اور یہ اضافی VRAM کو idle چھوڑنے کے بجائے اسے بیک وقت زیادہ requests چلانے کے لیے استعمال کرتا ہے۔ August 2026 تک documented install اور launch کے لیے 2 commands درکار ہیں:
uv pip install vllm --torch-backend=auto
vllm serve Qwen/Qwen2.5-1.5B-Instructcurl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2.5-1.5B-Instruct",
"messages": [{"role": "user", "content": "Say hello."}]
}'اگر reply میں choices array شامل ہو تو اس کا مطلب ہے کہ server چل رہا ہے اور model load ہو چکا ہے۔ load کے دوران 2 اہم knobs --max-num-seqs ہیں، یعنی "ایک iteration میں process کیے جانے والے sequences کی زیادہ سے زیادہ تعداد"، اور --max-num-batched-tokens، یعنی "ایک iteration میں process کیے جانے والے tokens کی زیادہ سے زیادہ تعداد"۔ پہلا knob concurrency کی حد مقرر کرتا ہے۔ دوسرا chunked prefill budget ہے جس کی وضاحت پہلے کی گئی ہے۔
تقریباً چار سے کم requests زیرِ عمل ہوں، یا ایسا کوئی box ہو جس میں supported GPU موجود نہ ہو، تو vLLM پیچیدگی بڑھاتا ہے لیکن فائدہ کم دیتا ہے۔ اسے CUDA-class card درکار ہوتا ہے اور startup کے وقت زیادہ تر memory مختص کر لیتا ہے، جو 4 سے 8 GB VPS پر مناسب تبادلہ نہیں۔ وہاں مختصر context رکھنے والا چھوٹا model اور آپ کے زیرِ انتظام queue بہتر انتخاب ہے۔ Ollama اور vLLM بطور serving engines کیسے مختلف ہیں میں اس انتخاب کی مکمل وضاحت ہے، جبکہ VPS پر Qwen 3 8B چلانا دکھاتا ہے کہ ایک اضافی user شامل کرنے سے پہلے mid-sized model کو کن وسائل کی ضرورت ہوتی ہے۔
دیومالائی بات جس tradeoff کو چھپا دیتی ہے
Continuous batching مجموعی throughput بڑھاتی ہے، اور عموماً median latency بھی بہتر کرتی ہے، کیونکہ queue میں موجود request جلد شروع ہو جاتی ہے۔ Tail latency اس کے برعکس بڑھتی ہے، لیکن اس حصے کا ذکر شاذ ہی کیا جاتا ہے۔
ہر step میں ایک اضافی sequence تھوڑا سا کام بڑھاتی ہے، اس لیے batch کے بھرنے کے ساتھ سب کے لیے ITL بڑھتا ہے۔ نئی آنے والی request کا prefill، step کا وہ حصہ استعمال کرتا ہے جو بصورتِ دیگر streaming users کو ملتا۔ Cache pressure کی صورت میں scheduler preempt کرتا ہے، جس سے آدھی تیار شدہ request اپنے prefill کے آغاز پر واپس چلی جاتی ہے۔
Chat UI میں averages نہیں بلکہ tails نمایاں ہوتے ہیں۔ جملے کے درمیان stream کا دو seconds کے لیے رک جانا، مجموعی completion time اچھا ہونے کے باوجود، سسٹم کو ناقص ظاہر کرتا ہے۔ متوقع load کے تحت p95 TTFT اور p95 ITL measure کریں، اور mean tokens per second کو experience کی وضاحت کے بجائے capacity number سمجھیں۔
عملی setting اسی نتیجے سے نکلتی ہے۔ Concurrency کو memory کی اجازت سے قدرے کم cap کریں، تاکہ engine کو کبھی preempt نہ کرنا پڑے۔ مختصر اور قابلِ پیش گوئی queue، ایسی گہری batch سے بہتر ہے جو مسلسل thrash کرے، کیونکہ وہ user زیادہ مطمئن ہوتا ہے جو چار seconds انتظار کے بعد smooth streaming دیکھے، بہ نسبت اس user کے جو فوراً شروع کرے مگر دو مرتبہ رک جائے۔
سست رفتاری کی صورت میں کیا جانچیں
ہر صارف کے لیے رفتار معمول کے مطابق ہے، لیکن انتظار طویل ہے۔ یہ queue ہے، رفتار کا مسئلہ نہیں۔ پہلے parallel setting جانچیں۔ model درست طور پر ایک وقت میں ایک request process کر رہا ہے۔
Ollama سے HTTP 503 موصول ہو رہا ہے۔ queue بھر چکی ہے۔ یا تو box واقعی اپنی capacity تک پہنچ چکا ہے، یا OLLAMA_MAX_QUEUE کو جان بوجھ کر کم رکھا گیا ہے تاکہ load کم کیا جا سکے؛ یہی مطلوبہ رویہ ہے۔
CPU box پر load بڑھنے کے ساتھ tokens per second بہت کم ہو جاتا ہے۔ مسئلہ پیش آنے کے دوران vmstat 1 چلائیں۔ si اور so columns میں nonzero values کا مطلب ہے کہ machine swapping کر رہی ہے، اس لیے ہر token کے لیے weights disk سے پڑھے جا رہے ہیں۔ کوئی configuration change اس مسئلے کو حل نہیں کر سکتی۔ model size یا slot count کم کریں۔
دس میں سے ایک صارف باقی صارفین کے مقابلے میں بہت زیادہ انتظار کرتا ہے۔ vLLM log میں preempted تلاش کریں۔ عموماً وجہ preemption اور اس کا recompute ہوتی ہے۔ اس کا مطلب ہے کہ آپ کی اجازت یافتہ context length کے لیے cache پر ضرورت سے زیادہ load ہے۔
server idle ہونے کے باوجود TTFT خراب ہے۔ یہ prefill کا مسئلہ ہے، concurrency کا نہیں۔ پہلے token کے ظاہر ہونے سے پہلے طویل prompts حقیقی وقت لیتے ہیں، اس لیے hardware جانچنے سے پہلے prompt size اور prefix caching دیکھیں۔ اگر طویل انتظار صرف خاموش وقفے کے بعد واپس آنے والے پہلے شخص کو ہوتا ہے اور اس کے بعد سب کے لیے رفتار معمول پر رہتی ہے، تو یہ prefill نہیں ہے۔ اس کے بجائے Ollama model کو unload کر کے weights دوبارہ disk سے پڑھ رہا ہے۔ اسے مسترد کرنے کے لیے requests کے درمیان model کو resident رکھنا آزمائیں۔
FAQ
دوسرا شخص استعمال کرے تو میرا self-hosted LLM سست کیوں ہو جاتا ہے؟
اکثر یہ سست نہیں ہوتا بلکہ قطار میں چلا جاتا ہے۔ Ollama میں OLLAMA_NUM_PARALLEL کی قدر 1 ہوتی ہے، اس لیے دوسری درخواست پہلی درخواست کے آخری token کے اخراج تک انتظار کرتی ہے۔ دونوں صورتوں میں فرق کرنے کے لیے ایک صارف کے stream کا وقت نوٹ کریں جبکہ دوسرا انتظار کر رہا ہو۔ اگر دوسرا شروع ہونے کے بعد اس کی tokens per second رفتار معمول کے مطابق ہو تو مسئلہ queue کا ہے، اور parallel count بڑھانے سے حل ہو جاتا ہے۔ اگر دونوں streams نصف رفتار سے چلیں تو آپ واقعی memory bandwidth تقسیم کر رہے ہیں، اور یہ hardware کی حد ہے۔
ایک چھوٹا GPU کتنے concurrent users کو سروس دے سکتا ہے؟
صارفین کی تعداد نہیں، memory شمار کریں۔ پہلے weights، پھر KV cache رکھیں۔ فی active conversation، فی token اس کی لاگت 2 times layers times key/value heads times head dimension times bytes ہوتی ہے۔ ایک عام 8B model میں 36 layers، 8 key/value heads اور head dimension 128 ہو تو 16-bit میں فی token تقریباً 144 KiB درکار ہوتے ہیں۔ اس لیے 8,192 token کی conversation کو تقریباً 1.2 GB درکار ہوتی ہے۔ 16-bit میں یہ model رکھنے والے 24 GB card میں cache کے لیے تقریباً 6 GB باقی رہتی ہے۔ یہ full context پر تقریباً پانچ conversations کے لیے کافی ہے، یا context مختصر کرنے پر اس سے زیادہ کے لیے۔
کیا continuous batching ہر صارف کا جواب سست کر دیتی ہے؟
Median latency عموماً بہتر ہوتی ہے، کیونکہ requests کو پورے batch کے مکمل ہونے تک انتظار نہیں کرنا پڑتا۔ Tail latency خراب ہو جاتی ہے۔ ہر اضافی sequence ہر decoding step میں مزید کام شامل کرتی ہے۔ نئی آنے والی request streaming users کے ایک step کا کچھ حصہ prefill کے لیے لے لیتی ہے، اور preempted request کو دو مرتبہ prefill کرنا پڑتا ہے۔ اوسط کے بجائے p95 inter-token latency ناپیں، کیونکہ chat window میں pauses نمایاں ہوتی ہیں جبکہ average انہیں چھپا دیتا ہے۔
کیا مجھے OLLAMA_NUM_PARALLEL بڑھانا چاہیے یا vLLM استعمال کرنا چاہیے؟
پہلے parallel count بڑھائیں۔ یہ مفت ہے، اور صرف ایک drop-in file درکار ہوتی ہے۔ عام صورت میں اس سے مسئلہ حل ہو جاتا ہے جہاں چار افراد ایک طویل جواب کے پیچھے قطار میں کھڑے ہوں۔ حد memory ہے۔ parallel requests اس context کو کئی گنا بڑھا دیتی ہیں جسے آپ کو برقرار رکھنا ہوتا ہے، اس لیے دیکھیں کہ layers CPU پر spill تو نہیں ہو رہیں۔ جب آپ کے GPU میں کافی VRAM دستیاب ہو اور تقریباً چار سے زیادہ requests واقعی بیک وقت in flight ہوں تو vLLM استعمال کریں۔ اسی مرحلے پر paged cache اور per-token scheduling اپنی لاگت سے زیادہ فائدہ دیتے ہیں۔
کیا زیادہ CPU cores سست LLM server کو ٹھیک کر سکتے ہیں؟
صارفین کو محسوس ہونے والے بنیادی حصے کے لیے نہیں۔ Decode ہر token کے لیے پورا model memory سے پڑھتا ہے، اس لیے یہ RAM bandwidth سے محدود ہوتا ہے۔ Bandwidth بھر جانے کے بعد اضافی cores مزید فائدہ نہیں دیتے۔ Prefill cores کے ساتھ scale ہوتا ہے، اس لیے زیادہ cores طویل prompts پر time to first token کم کرتے ہیں۔ 4 سے 8 GB VPS پر عموماً بنیادی رکاوٹ memory capacity ہوتی ہے۔ مؤثر حل زیادہ vCPUs کے بجائے چھوٹا model یا مختصر context استعمال کرنا ہے۔