SSD Nodes Learn 🎉 VPS $4.99/মাস থেকে
নির্দেশিকা Matt Connorদ্বারা Matt Connor

5 জন ব্যবহারকারী হলে self-hosted LLM ধীর কেন

একজন ব্যবহারকারী স্বাভাবিক থাকলেও 5 জনে কেন LLM থেমে যায়? Ollama-এর default parallel request 1, batching, KV cache, prefill ও queue depth-ই আসল সীমা নির্ধারণ করে।

আরও ব্যবহারকারী যুক্ত হলে self-hosted LLM ধীর হয়ে যায় কেন?

5 জন ব্যবহারকারী একসঙ্গে যুক্ত হলে self-hosted LLM থেমে থেমে চলে, কারণ সার্ভারটি এখনও একবারে একটি উত্তর তৈরি করছে এবং বাকি চারটি অনুরোধ সারিতে অপেক্ষা করছে। Ollama-এর documentation-এ default আচরণটি স্পষ্টভাবে বলা আছে: OLLAMA_NUM_PARALLEL হলো “প্রতিটি model একই সময়ে যতগুলো parallel request process করবে তার সর্বোচ্চ সংখ্যা; default 1।” কোনো ত্রুটি ঘটেনি। 5 জনের মধ্যে 4 জন তাদের পালার জন্য অপেক্ষা করছে।

সমাধানটি সাধারণত আরও শক্তিশালী server নেওয়া নয়। প্রয়োজন এমন একটি serving engine, যা একই forward pass-এ model-এর মাধ্যমে অনেকগুলো request process করতে পারে। একই সঙ্গে সবার conversation ধরে রাখার জন্য পর্যাপ্ত অতিরিক্ত memory-ও দরকার। উভয় অংশই গুরুত্বপূর্ণ। তবে আপনার সর্বোচ্চ সীমা আসলে দ্বিতীয় অংশটিই নির্ধারণ করে।

প্রতিটি অনুরোধ যে দুটি ধাপের মধ্য দিয়ে যায়

Prefill পুরো prompt একসঙ্গে পড়ে এবং এর জন্য attention cache তৈরি করে। প্রতিটি prompt token মডেলের মধ্য দিয়ে একসঙ্গে যায়। তাই prefill হলো একটি বড় matrix multiply, এবং এটি arithmetic throughput দ্বারা সীমাবদ্ধ। এরপর decode একবারে একটি token লিখে উত্তর তৈরি করে। প্রতিটি token-এর জন্য মডেলের সম্পূর্ণ weights আবার memory থেকে পড়তে হয়, কিন্তু ওই একক token-এর ওপর সম্পাদিত arithmetic খুবই কম। তাই decode memory bandwidth দ্বারা সীমাবদ্ধ।

এই অসমতাই batching কার্যকর হওয়ার মূল কারণ। একজন user-এর জন্য decoding করতে প্রতি token-এ ধরুন 5 GB weights পড়তে হয়, আর অধিকাংশ arithmetic unit নিষ্ক্রিয় থাকে। দ্বিতীয় একটি request যোগ করলে engine একই 5 GB একবার পড়ে এবং তা ব্যবহার করে দুটি token গণনা করে। দ্বিতীয় user-এর জন্য প্রয়োজনীয় অতিরিক্ত সময় প্রায় নেই। অনুরোধগুলো কঠোরভাবে একটির পর একটি পরিবেশন করলে এই সুবিধা নষ্ট হয়।

একজন user কী অনুভব করেন, তা দুটি সংখ্যা দিয়ে বোঝানো যায়। TTFT (time to first token) হলো queue wait এবং prefill-এর মোট সময়। ITL (inter-token latency) হলো streamed token-গুলোর মধ্যবর্তী ব্যবধান, এবং এটি decode দ্বারা নির্ধারিত হয়। একটি ধীর server সাধারণত এই দুটির কোনো একটিতে ধীর হয়, আর দুই ক্ষেত্রের সমাধান এক নয়।

Static batching-এর কারণে ধীরতম উত্তরের জন্য সবাইকে অপেক্ষা করতে হয়

Static batching হলো সরল পদ্ধতি। application code-এ নিজে request group করলে আপনি সাধারণত এটিই পান। engine Nটি request সংগ্রহ করে একসঙ্গে চালায় এবং group-এর মধ্যে সবচেয়ে দীর্ঘ generation শেষ না হওয়া পর্যন্ত প্রতিটি slot ধরে রাখে।

একজন user 1,200 token-এর summary চাইলে batch-এর চারটি একলাইন উত্তর আটকে থাকে, কারণ batch-এর সবচেয়ে ধীর সদস্যের কাজ শেষ না হওয়া পর্যন্ত batch কোনো slot ছাড়ে না।

এর ফলে দুটি খরচ হয়। সম্পন্ন হওয়া sequence-গুলো এমন slot দখল করে রাখে, যেখানে আর কোনো কার্যকর computation হয় না। তাই output length ভিন্ন হলে effective throughput কমে যায়, আর chat-এর output length অনেকটাই ভিন্ন হয়। Batch তৈরি হওয়ার এক step পরে আসা কোনো request পুরো batch খালি হওয়া পর্যন্ত অপেক্ষা করে। এরপরই শুধু তার prefill শুরু হয়। অর্থাৎ, তার TTFT নির্ধারিত হয় অন্য একজনের দীর্ঘ লেখার কারণে।

প্রতিটি token-এ continuous batching request গ্রহণ ও অবসর দেয়

Continuous batching একটি decoding step-এর স্তরে scheduling করে। প্রতিটি step-এর পরে scheduler সদ্য stop token উৎপন্ন করা sequence বাদ দেয়, তারপর খালি slot-এ অপেক্ষমাণ request গ্রহণ করে। step 40-এ শেষ হওয়া একটি reply তার slot step 40-এই খালি করে, batch শেষ হওয়ার সময় নয়।

এটি কোনো বিরল পদ্ধতি নয়। llama-server-এ -cb, --cont-batching-কে “continuous batching (অর্থাৎ dynamic batching) চালু করা হবে কি না (default: enabled)” হিসেবে নথিভুক্ত করা হয়েছে, এবং vLLM এই ধারণার ওপর ভিত্তি করেই তৈরি। Ollama-ও parallel request পরিবেশন করে। তবে default মানটি সংখ্যা একটিতে সীমাবদ্ধ রাখে। তাই অনেকেই মনে করেন তাদের hardware concurrency চালাতে পারে না, যদিও প্রকৃতপক্ষে তাদের configuration-ই তা নিষিদ্ধ করেছিল।

প্রকাশিত continuous batching ফলাফল সাধারণত এমন datacenter card-এ মাপা হয়, যেখানে অতিরিক্ত compute capacity এবং cache-এর জন্য কয়েক দশ gigabyte memory—দুটিই থাকে। ওই ফলাফলের ধরন আপনার system-এও প্রযোজ্য। তবে ফলাফলের মাত্রা একই হবে না। নিচের memory section-এ এর কারণ ব্যাখ্যা করা হয়েছে।

Prefill একই compute resource-এর জন্য decode-এর সঙ্গে প্রতিযোগিতা করে

চারটি উত্তর streaming অবস্থায় থাকলে নতুন কোনো request আসার পর সেটি আগে prefill করতে হয়, আর prefill compute-intensive। Scheduler যদি ওই prefill-এর জন্য আলাদা একটি step দেয়, তাহলে সেই সময় চারজন streaming user কোনো token পান না। দীর্ঘ prompt হলে প্রতিটি খোলা window-তে এটি স্পষ্ট বিরতি হিসেবে দেখা যায়। অন্য কেউ send চাপলেই server সাময়িকভাবে থেমে যায়—এ কথা বলার সময় মানুষ যে stutter বোঝায়, এটিই তা।

Chunked prefill একটি দীর্ঘ prompt-কে কয়েকটি অংশে ভাগ করে এবং চলমান decode-এর একই step-এর সঙ্গে প্রতিটি অংশ মিশিয়ে দেয়। vLLM-এর tuning guide-এ tradeoff-টি সরাসরি বলা হয়েছে: ছোট chunk budget "কম prefill decode-কে ধীর করে, তাই আরও ভালো ITL দেয়", আর বেশি মান "batch-এ আরও বেশি prefill token process করা যায় বলে ভালো time to first token (TTFT) দেয়"। আপনি ঠিক করছেন কোন অভিজ্ঞতাকে অগ্রাধিকার দেবেন: যে ব্যক্তি reply শুরু হওয়ার অপেক্ষায় আছেন, নাকি যারা text stream হতে দেখছেন।

Prompt-এর দৈর্ঘ্য নির্ধারণ করে এই প্রভাব কতটা হবে। 6,000 token-এর একটি prompt এবং 200 token-এর একটি answer হলে, 200টি decode step-এর বিপরীতে prefill-এর জন্য 6,000 token-এর কাজ করতে হয়। Retrieval-augmented chat এবং দীর্ঘ system prompt উভয়ই আপনাকে এই পরিস্থিতিতে নিয়ে যেতে পারে। ফলে prefill আর সামান্য অতিরিক্ত কাজ থাকে না; এটিই ব্যবহারকারীদের অপেক্ষা করায়। দীর্ঘ অংশটি পুনরাবৃত্ত হলে prefix caching সহায়তা করে: vLLM --enable-prefix-caching প্রকাশ করে, যা প্রতিটি request-এর জন্য shared prompt prefix পুনরায় গণনা না করে তার cache পুনরায় ব্যবহার করে।

প্রথমে শেষ হয়ে যায় KV cache

প্রতিটি সক্রিয় কথোপকথনের প্রতিটি token মডেলের প্রতিটি layer-এ একটি key vector এবং একটি value vector রেখে যায়। এটিই KV cache (key/value cache)। নতুন প্রতিটি token-এর জন্য পুরো prompt আবার গণনা না করেই decode করা সম্ভব হয় এই cache-এর কারণে। প্রতি token-এর জন্য এর আকার মডেলের গঠন দ্বারা নির্ধারিত: 2 (একটি key এবং একটি value) গুণ layer-এর সংখ্যা, গুণ key/value head-এর সংখ্যা, গুণ head dimension, গুণ প্রতি value-এর byte সংখ্যা। এই সংখ্যাগুলো মডেলের config.json থেকে পড়ুন।

একবার হিসাব করলে সর্বোচ্চ সীমা আর অস্পষ্ট থাকে না। 36টি layer, 8টি key/value head এবং 128 head dimension-এর একটি সাধারণ 8B মডেলে cache 16-bit-এ রাখলে প্রতি token-এর খরচ হয় 2 36 8 128 2 bytes। এর পরিমাণ 147,456 bytes, অর্থাৎ প্রায় 144 KiB। একটি 8,192 token-এর কথোপকথনের জন্য তাই প্রায় 1.2 GB cache প্রয়োজন। 5টি কথোপকথনের জন্য weights-এর অতিরিক্ত প্রায় 6 GB লাগে। কতজন user একসঙ্গে চলতে পারবেন, তার বাস্তব উত্তর এটাই।

Concurrency context-কে গুণ করে, এবং tools-এ তা স্পষ্টভাবে দেখা যায়। Ollama-এর FAQ-এ বলা হয়েছে: "একটি নির্দিষ্ট model-এর জন্য parallel request processing করলে parallel request-এর সংখ্যা অনুযায়ী context size বাড়ে। যেমন, 4টি parallel request সহ 2K context হলে 8K context এবং অতিরিক্ত memory allocation প্রয়োজন হবে।" প্রয়োজনীয় RAM, OLLAMA_NUM_PARALLEL-কে OLLAMA_CONTEXT_LENGTH দিয়ে গুণ করলে যে মান পাওয়া যায়, সেই অনুযায়ী বাড়ে। llama-server-এ -c দিয়ে নির্ধারিত context, -np slot-এর মধ্যে ভাগ হয়। তাই শুধু slot-এর সংখ্যা বাড়ালে প্রতিটি request-এর জন্য উপলভ্য context কমে যায়। অনুমান না করে startup log থেকে প্রতি slot-এর context পড়ুন।

vLLM আগে থেকেই memory বরাদ্দ করে। --gpu-memory-utilization (default 0.92) হলো "model executor-এর জন্য ব্যবহার করা GPU memory-এর ভগ্নাংশ"। weights বরাদ্দের পরে যা অবশিষ্ট থাকে, তা paged KV pool হিসেবে ব্যবহৃত হয়। এই pool-এ স্থান কমে গেলে scheduler request ব্যর্থ না করে একটি request 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-এ default preemption mode হলো RECOMPUTE। তাই evict করা request-এর cache বাতিল হয় এবং পুনরায় অনুমোদন পাওয়ার পরে request-টি আবার prefill করা হয়। একই কাজ 2 বার সম্পন্ন হয়। Documentation-এ সতর্ক করা হয়েছে যে "preemption এবং recomputation end-to-end latency-তে বিরূপ প্রভাব ফেলতে পারে"। আপনার average latency স্বাভাবিক থাকলেও কোনো একজন user কেন অন্য সবার তুলনায় অনেক বেশি সময় অপেক্ষা করেছে, তা বোঝানোর জন্য এই log line-টিই সবচেয়ে কার্যকর। cumulative count log করতে disable_log_stats=False সেট করুন, অথবা vLLM যে Prometheus metrics প্রকাশ করে সেখান থেকে preemption counter পড়ুন।

2, 5 এবং 20 জন একসঙ্গে ব্যবহারকারীতে কী পরিবর্তন হয়

দুইজন ব্যবহারকারী। অতিরিক্ত cache-সহ GPU-তে এর প্রভাব প্রায় বোঝাই যায় না, কারণ দ্বিতীয় decode stream প্রথমটির সঙ্গে সামান্য অতিরিক্ত সময়ে চলতে পারে। 4 থেকে 8 GB RAM-সহ শুধু CPU-নির্ভর VPS-এ এটি বিনামূল্যে নয়: উভয় stream একই সীমিত সংখ্যক vCPU এবং একই RAM bandwidth ভাগ করে ব্যবহার করে। ফলে প্রতিটি ব্যবহারকারী প্রায় অর্ধেক tokens per second পান, আর তুলনামূলকভাবে ছোট budget-এর ওপর cache-এর চাহিদা দ্বিগুণ হয়।

পাঁচজন ব্যবহারকারী। এখানে default আর যথেষ্ট থাকে না, এবং সমস্যা শুরুতে queue-সংক্রান্ত হয়। OLLAMA_NUM_PARALLEL 1-এ সেট করা থাকলে, চারজনকে দীর্ঘ উত্তর চাওয়া ব্যবহারকারীর কাজ শেষ হওয়া পর্যন্ত অপেক্ষা করতে হয়। তবে নিজের turn এলে প্রত্যেকে স্বাভাবিক গতি পান। parallel count বাড়ালে সমস্যার ধরন বদলে যায়: প্রতিটি 8K context-সহ পাঁচটি slot-এর জন্য 40K token cache প্রয়োজন। এটি VRAM-এ না ধরলে engine layer-গুলো system RAM-এ offload করে। RAM-এও না ধরলে মেশিন swap ব্যবহার শুরু করে এবং tokens per second দ্রুত কমে যায়।

বিশজন ব্যবহারকারী। Chat UI-তে বিশজন মানুষ সাধারণত বিশটি concurrent request তৈরি করেন না। Hardware কেনার আগে এটিই সবচেয়ে গুরুত্বপূর্ণ বিষয়। একজন ব্যক্তি একটি উত্তর পড়ে পরের turn দেওয়ার আগে 20 থেকে 60 seconds চিন্তা করেন, তাই তাঁর session-এর বেশিরভাগ সময় idle থাকে। কিন্তু বিশটি agent, অথবা বিশটি document summarisation job, idle time ছাড়াই বিশটি বাস্তব stream তৈরি করে। এটি ভিন্ন ধরনের মেশিনের প্রয়োজন করে।

আপনার ব্যবহারকারীরা কি concurrent, নাকি শুধু logged in?

আকার নির্ধারণের আগে চলমান request-এর সংখ্যা হিসাব করুন। হিসাবটি সরল: চলমান request = ব্যবহারকারীর সংখ্যা × প্রতি turn-এ generation-এর জন্য ব্যয় হওয়া সেকেন্ড ÷ দুই turn-এর মধ্যবর্তী সেকেন্ড।

  1. প্রথমে আপনার নিজের single-stream speed মাপুন, prefill এবং decode দুটিই। অন্য কারও card-এর সংখ্যা ব্যবহার করবেন না: নিজের box-এ প্রতি সেকেন্ডে token মাপুন এবং প্রাপ্ত মান ব্যবহার করুন।
  2. duty cycle অনুমান করুন। 20 জন chat user, প্রতি turn-এ generation-এর জন্য 12 সেকেন্ড, এবং প্রতি 90 সেকেন্ডে একটি turn হলে 20 * 12 / 90, অর্থাৎ প্রায় 2.7টি চলমান request।
  3. slot count এই সংখ্যার চেয়ে সামান্য বেশি নির্ধারণ করুন। এরপর memory-এর সঙ্গে মিলিয়ে দেখুন: slot count × প্রতি request-এর context আপনার কাছে বাস্তবে থাকা cache token-এর মধ্যে থাকতে হবে।
  4. queue ছোট রাখুন, যাতে অতিরিক্ত request দ্রুত এবং স্পষ্টভাবে ব্যর্থ হয়।

উপলভ্য cache token = weights load করার পরের free memory ÷ আগের section-এ দেওয়া প্রতি-token cost। 8B model 16-bit-এ চালানো একটি 24 GB card weights-এর জন্য প্রায় 16 GB ব্যবহার করে। Default utilisation-এ usable cache থাকে আনুমানিক 6 GB, যা প্রায় পাঁচটি 8K conversation-এর জন্য যথেষ্ট। আরও বেশি conversation fit করাতে প্রতি request-এর context ছোট করুন, অথবা cache 8-bit-এ সংরক্ষণ করুন (llama-server নিতে --cache-type-k q8_0 লাগে)। উভয় পদ্ধতিই কিছু সুবিধা ছেড়ে concurrency বাড়ায়। Hardware কেনার আগে এই trade-off-এর বাস্তব বিশ্লেষণটি পড়া উচিত: GPU VPS-এর খরচ API token-এর খরচের তুলনায় কখন সমান হয়

যেখানে Ollama-এর ডিফল্ট সেটিং যথেষ্ট নয়

service unit-এর মাধ্যমে parallel count বাড়ান, কারণ shell export systemd-managed 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 ps

systemctl show চালালে আপনার সদ্য সেট করা তিনটি variable দেখানোর কথা। তা না হলে drop-in সংরক্ষিত হয়নি, এবং এরপর আপনি যা-ই করুন না কেন তাতে কোনো ফল হবে না। ollama ps এরপর লোড করা model-এর size দেখায়। এই size শুধু weights-এর size-এর চেয়ে বড়, কারণ 8,192 token-এর 4টি slot-এর জন্য model-এর পাশে 32,768 token-এর cache সংরক্ষিত থাকে। আপনি যদি model-এর সম্পূর্ণ অংশ GPU-তে থাকার আশা করেন, কিন্তু PROCESSOR column-এ model-এর কিছু অংশ CPU-তে দেখায়, তার অর্থ card-এ অবশিষ্ট memory-এর চেয়ে আপনি বেশি cache চেয়েছেন। দুটি সংখ্যার মধ্যে একটি কমান।

queue-এর default নিয়েও আবার ভাবা দরকার। Ollama সর্বোচ্চ OLLAMA_MAX_QUEUEটি request queue-তে রাখে, এবং “default হলো 512”। এর বেশি হলে এটি “server overloaded নির্দেশ করে 503 error” ফেরত দেয়। একসঙ্গে 4টি request পরিবেশন করা server-এ 512-দৈর্ঘ্যের queue এমন প্রতিশ্রুতি তৈরি করে, যা রাখা সম্ভব নয়, কারণ queue-তে 300তম অবস্থানে থাকা client-এর পালা আসার অনেক আগেই timeout হয়ে যায়। ছোট queue এমন একটি error ফেরত দেয়, যা আপনার application retry করতে বা report করতে পারে। উত্তর না পাওয়া spinner চলতে থাকার চেয়ে এটি ভালো।

বাস্তবভাবে পরীক্ষা করুন। একই সময়ে দুটি terminal থেকে দুটি request পাঠিয়ে দুটির অবস্থা দেখুন। দ্বিতীয় request-এর output প্রথমটি শেষ না হওয়া পর্যন্ত না এলে parallel setting কার্যকর হয়নি।

যখন একটি প্রকৃত serving engine-এর খরচ উঠে আসতে শুরু করে

যখন আপনার headroom-সহ একটি GPU থাকে এবং মোটামুটি চারটির বেশি request সত্যিই in flight থাকে, তখন vLLM-এর অতিরিক্ত setup সার্থক হয়। এর scheduler token অনুযায়ী কাজ করে, এর cache paged পদ্ধতিতে পরিচালিত হয় বলে অব্যবহৃত fragment পুনরায় ব্যবহার করা যায়, এবং এটি অব্যবহৃত অবস্থায় VRAM ফেলে না রেখে সেই অতিরিক্ত VRAM-কে concurrency-তে রূপান্তর করে। August 2026 অনুযায়ী নথিভুক্ত install ও launch দুটি command:

uv pip install vllm --torch-backend=auto
vllm serve Qwen/Qwen2.5-1.5B-Instruct
curl 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."}]
    }'

choices array-সহ কোনো reply পাওয়া মানে server চালু হয়েছে এবং model load হয়েছে। Load-এর সময় গুরুত্বপূর্ণ দুটি knob হলো --max-num-seqs, অর্থাৎ "একটি iteration-এ process করা যাবে এমন sequence-এর সর্বোচ্চ সংখ্যা", এবং --max-num-batched-tokens, অর্থাৎ "একটি iteration-এ process করা যাবে এমন token-এর সর্বোচ্চ সংখ্যা"। প্রথমটি concurrency সীমিত করে। দ্বিতীয়টি আগে বর্ণিত chunked prefill budget নির্ধারণ করে।

চারটির কম request in flight থাকলে, অথবা supported GPU না থাকা কোনো box-এ, vLLM অতিরিক্ত complexity তৈরি করে কিন্তু খুব কম সুবিধা দেয়। এটি CUDA-class card প্রত্যাশা করে এবং startup-এর সময় memory-এর বেশিরভাগ দখল করে নেয়, যা 4 থেকে 8 GB VPS-এর ক্ষেত্রে উপযুক্ত trade নয়। সে ক্ষেত্রে নিয়ন্ত্রণাধীন queue-সহ ছোট model এবং ছোট context ব্যবহার করাই ভালো। Ollama এবং vLLM serving engine হিসেবে কীভাবে আলাদা-এ এই পছন্দটি বিস্তারিত ব্যাখ্যা করা হয়েছে, আর VPS-এ Qwen 3 8B চালানো দেখায়, এক জন অতিরিক্ত user যোগ করার আগে একটি mid-sized model কী পরিমাণ resource চায়।

লোকমুখে প্রচলিত ধারণা যে দিকটি আড়াল করে

Continuous batching মোট throughput বাড়ায় এবং সাধারণত median latency-ও কমায়, কারণ queue-তে থাকা request দ্রুত শুরু হয়। Tail latency বিপরীত দিকে যায়, কিন্তু এই দিকটি খুব কমই উল্লেখ করা হয়।

একটি step-এ প্রতিটি অতিরিক্ত sequence সামান্য কাজ যোগ করে। তাই batch পূর্ণ হওয়ার সঙ্গে সঙ্গে সবার ITL বাড়ে। নতুন কোনো arrival-এর prefill এমন একটি step-এর সময়ের অংশ নেয়, যা না হলে streaming user-রা পেতেন। Cache-এর ওপর চাপ পড়লে scheduler preempt করে। ফলে আংশিকভাবে generate হওয়া request-কে তার prefill-এর শুরুতে ফেরত পাঠাতে হয়।

একটি chat UI গড় latency নয়, tail latency দেখায়। বাক্যের মাঝখানে কোনো stream দুই সেকেন্ড থেমে গেলে মোট completion time ভালো হলেও সেটিকে ত্রুটিপূর্ণ মনে হয়। আপনি যে load প্রত্যাশা করছেন, তার অধীনে p95 TTFT এবং p95 ITL মাপুন। Mean tokens per second-কে capacity number হিসেবে বিবেচনা করুন, user experience-এর বর্ণনা হিসেবে নয়।

এর ভিত্তিতেই ব্যবহারিক setting নির্ধারণ করা যায়। Memory যতটা অনুমতি দেয়, তার সামান্য নিচে concurrency সীমাবদ্ধ রাখুন, যাতে engine-কে কখনো preempt করতে না হয়। গভীর এমন একটি batch-এর চেয়ে সংক্ষিপ্ত ও পূর্বানুমানযোগ্য queue ভালো, যা বারবার thrash করে। কারণ একজন user চার সেকেন্ড অপেক্ষা করে পরে মসৃণভাবে stream পেলে, এমন user-এর অভিজ্ঞতা ভালো হয় যার request সঙ্গে সঙ্গে শুরু হলেও দুইবার থেমে যায়।

ধীরগতির হলে কী পরীক্ষা করবেন

প্রতিটি অনুরোধ স্বাভাবিক, কিন্তু অপেক্ষার সময় দীর্ঘ। এটি queue-এর সমস্যা, গতি কমার সমস্যা নয়। প্রথমে parallel setting পরীক্ষা করুন। মডেল সঠিকভাবে request পরিবেশন করছে, তবে একবারে একটি request।

Ollama থেকে HTTP 503। queue পূর্ণ। সার্ভারটি সত্যিই capacity-তে পৌঁছেছে, অথবা OLLAMA_MAX_QUEUE ইচ্ছাকৃতভাবে কম সেট করা হয়েছে load কমানোর জন্য। এই setting-এর উদ্দেশ্যই সেটি।

CPU server-এ load বাড়লে tokens per second কমে যায়। সমস্যা চলাকালে vmstat 1 চালান। si এবং so column-এ nonzero মান থাকলে machine swapping করছে। ফলে প্রতিটি token-এর সময় disk থেকে weights পড়তে হচ্ছে। কোনো configuration change এতে সমাধান দেবে না। model size অথবা slot count কমান।

দশজনের মধ্যে একজন অন্যদের তুলনায় অনেক বেশি সময় অপেক্ষা করে। vLLM log-এ preempted খুঁজুন। সাধারণত এর কারণ preemption এবং তার recompute। এর অর্থ, আপনি যে context length অনুমোদন করেছেন তার তুলনায় cache অতিরিক্ত ব্যবহৃত হচ্ছে।

Server idle থাকা সত্ত্বেও TTFT খারাপ। এটি concurrency নয়, prefill-এর সমস্যা। প্রথম token দেখা যাওয়ার আগে দীর্ঘ prompt প্রক্রিয়াকরণে বাস্তব সময় লাগে। তাই hardware পরীক্ষা করার আগে prompt size এবং prefix caching দেখুন।

FAQ

দ্বিতীয় একজন ব্যবহারকারী LLM ব্যবহার করলে আমার self-hosted LLM ধীর হয়ে যায় কেন?

বেশিরভাগ সময় এটি আসলে ধীর হয় না। অনুরোধ queue-তে অপেক্ষা করে। Ollama-তে OLLAMA_NUM_PARALLEL-এর মান 1 থাকে, তাই দ্বিতীয় অনুরোধটি প্রথম অনুরোধের final token তৈরি হওয়া পর্যন্ত অপেক্ষা করে। একজন ব্যবহারকারীর stream চলার সময় অন্যজন অপেক্ষা করছে—এই অবস্থায় timing মেপে দুটি ঘটনা আলাদা করুন। অন্য ব্যবহারকারীর stream শুরু হওয়ার পর tokens per second স্বাভাবিক থাকলে এটি queue সমস্যা, এবং parallel count বাড়ালে সমাধান হবে। উভয় stream অর্ধেক গতিতে চললে memory bandwidth সত্যিই ভাগ হচ্ছে। এটি hardware limit।

একটি ছোট GPU কতজন concurrent user সামলাতে পারে?

ব্যবহারকারীর সংখ্যা নয়, memory হিসাব করুন। প্রথমে weights, এরপর KV cache। প্রতি active conversation-এর প্রতি token-এর জন্য KV cache-এর খরচ হলো 2 times layers times key/value heads times head dimension times bytes। 36 layers, 8 key/value heads এবং head dimension 128-সহ একটি সাধারণ 8B model 16-bit-এ প্রতি token-এ প্রায় 144 KiB ব্যবহার করে। তাই 8,192 token-এর একটি conversation-এর জন্য প্রায় 1.2 GB দরকার। 16-bit-এ model-টি ধারণ করা 24 GB card-এ cache-এর জন্য প্রায় 6 GB অবশিষ্ট থাকে। এতে full context-এ প্রায় পাঁচটি conversation রাখা যায়। context ছোট করলে আরও বেশি রাখা সম্ভব।

continuous batching কি প্রতিটি ব্যবহারকারীর reply ধীর করে?

Median latency সাধারণত কমে, কারণ request-গুলোকে পুরো batch শেষ হওয়া পর্যন্ত অপেক্ষা করতে হয় না। Tail latency বেড়ে যায়। প্রতিটি অতিরিক্ত sequence প্রতিটি decoding step-এ কাজ বাড়ায়। নতুন কোনো request এলে streaming user-দের জন্য একটি step-এর কিছু অংশ prefill-এ চলে যায়। Preempted request-কে আবার দুবার prefill করতে হয়। গড় latency নয়, p95 inter-token latency মাপুন। গড় মান যে বিরতিগুলো আড়াল করে, chat window-তে সেগুলো স্পষ্ট দেখা যায়।

আমার কি OLLAMA_NUM_PARALLEL বাড়ানো উচিত, নাকি vLLM-এ যাওয়া উচিত?

প্রথমে parallel count বাড়ান। এতে কোনো অতিরিক্ত খরচ নেই এবং একটি drop-in file-ই যথেষ্ট। একজন ব্যবহারকারীর দীর্ঘ উত্তরের পেছনে চারজন queue-তে অপেক্ষা করার সাধারণ সমস্যাটি এতে সমাধান হয়। সীমাবদ্ধতা হলো memory। Parallel request-এর সংখ্যা বাড়লে ধরে রাখতে হওয়া context-ও বাড়ে। তাই কোনো layer CPU-তে spill করছে কি না monitor করুন। GPU-তে অতিরিক্ত VRAM থাকলে এবং চারটির বেশি request সত্যিই একসঙ্গে in flight থাকলে vLLM-এ যান। এই অবস্থায় paged cache এবং per-token scheduling-এর সুবিধা সাধারণত তাদের খরচের চেয়ে বেশি হয়।

আরও বেশি CPU core কি ধীর LLM server ঠিক করতে পারে?

ব্যবহারকারীরা যে অংশটি সবচেয়ে বেশি অনুভব করেন, সেটির ক্ষেত্রে সাধারণত পারে না। প্রতিটি token-এর জন্য decode memory থেকে পুরো model পড়ে। তাই এটি RAM bandwidth দ্বারা সীমাবদ্ধ। Bandwidth পূর্ণ হয়ে গেলে অতিরিক্ত core আর কোনো উপকার করে না। Prefill অবশ্য core-এর সংখ্যার সঙ্গে scale করে। তাই দীর্ঘ prompt-এর ক্ষেত্রে আরও বেশি core first token পাওয়ার সময় কমায়। 4 থেকে 8 GB VPS-এ সাধারণত সীমাবদ্ধতা হলো memory capacity। তাই বেশি vCPU নয়, ছোট model বা ছোট context ব্যবহার করাই কার্যকর সমাধান।

#vllm#ollama#batching#throughput#self-hosted-ai