Self-hosted LLM வேகம் ஏன் குறைகிறது? தீர்வு என்ன?
ஒரே நேரத்தில் 5 பயனர்கள் பயன்படுத்தும்போது LLM வேகம் ஏன் குறைகிறது என்பதை விளக்குகிறோம். Batching, KV cache மற்றும் queue depth மூலம் உங்கள் server திறனை மேம்படுத்தும் வழிமுறைகள்.
அதிக பயனர்கள் வரும்போது self-hosted LLM ஏன் வேகம் குறைகிறது?
ஒரே நேரத்தில் 5 பயனர்கள் பயன்படுத்தும்போது self-hosted LLM வேகம் குறைகிறது, ஏனெனில் server ஒரு நேரத்தில் ஒரு பதிலை மட்டுமே உருவாக்குகிறது; மற்ற நால்வரும் வரிசையில் காத்திருக்கிறார்கள். Ollama-வின் ஆவணங்கள் இயல்புநிலை அமைப்பைப் பற்றி தெளிவாகக் கூறுகின்றன: OLLAMA_NUM_PARALLEL என்பது "ஒவ்வொரு மாதிரியும் ஒரே நேரத்தில் கையாளக்கூடிய அதிகபட்ச இணையான கோரிக்கைகளின் எண்ணிக்கை, இதன் இயல்புநிலை 1 ஆகும்." இதில் எந்தப் பிழையும் இல்லை. உங்கள் ஐந்து பயனர்களில் நால்வர் தங்கள் முறைக்காகக் காத்திருக்கிறார்கள்.
இதற்கான தீர்வு அரிதாகவே பெரிய server-ஐ வாங்குவதாக அமையும். இதற்குப் பதிலாக, பல கோரிக்கைகளை ஒரே forward pass-ல் மாதிரியின் வழியாகச் செலுத்தும் ஒரு serving engine தேவை. அத்துடன், அவ்வாறு செய்யும்போது அனைவரின் உரையாடல்களையும் நினைவகத்தில் (memory) வைத்திருக்கப் போதுமான அளவு கூடுதல் நினைவகம் தேவை. இந்த இரண்டு பகுதிகளுமே முக்கியம், இதில் இரண்டாவது பகுதிதான் உங்கள் பயன்பாட்டின் உச்ச வரம்பைத் தீர்மானிக்கிறது.
ஒவ்வொரு கோரிக்கையும் கடந்து செல்லும் இரண்டு நிலைகள்
Prefill என்பது முழு prompt-ஐயும் ஒரே நேரத்தில் படித்து, அதற்கான attention cache-ஐ உருவாக்குகிறது. அனைத்து prompt token-களும் ஒரே நேரத்தில் model-க்குள் செல்வதால், prefill என்பது ஒரு பெரிய matrix multiply ஆகும்; இது arithmetic throughput-ஆல் கட்டுப்படுத்தப்படுகிறது. Decode என்பது பதிலை ஒரு நேரத்தில் ஒரு token-ஆக எழுதுகிறது. ஒவ்வொரு token-க்கும் model-ன் முழு weights-ஐயும் மீண்டும் memory-யிலிருந்து படிக்க வேண்டியுள்ளது, ஆனால் அந்த ஒரு token-க்காக செய்யப்படும் arithmetic மிகச் சிறியது. Decode என்பது memory bandwidth-ஆல் கட்டுப்படுத்தப்படுகிறது.
இந்த சமச்சீரற்ற தன்மையே batching செயல்படுவதற்கான முழுமையான காரணமாகும். ஒரு பயனருக்காக decoding செய்யும்போது, ஒரு token-க்கு சுமார் 5 GB weights படிக்கப்படுகிறது, ஆனால் பெரும்பாலான arithmetic units சும்மா இருக்கின்றன. இரண்டாவது கோரிக்கையைச் சேர்க்கும்போது, engine அதே 5 GB-ஐ ஒருமுறை படித்து, அதிலிருந்து இரண்டு token-களைக் கணக்கிடுகிறது. இரண்டாவது பயனருக்குக் கூடுதல் நேரம் கிட்டத்தட்ட தேவைப்படுவதில்லை. கோரிக்கைகளை ஒவ்வொன்றாக வரிசையாகச் செயல்படுத்துவது இந்த நன்மையை வீணாக்கிவிடும்.
பயனர் உணரும் அனுபவத்தை இரண்டு எண்கள் விவரிக்கின்றன. TTFT (time to first token) என்பது queue wait மற்றும் prefill ஆகியவற்றின் கூட்டுத்தொகை ஆகும். ITL (inter-token latency) என்பது stream செய்யப்படும் token-களுக்கு இடையே உள்ள இடைவெளி ஆகும், இது decode-ஆல் தீர்மானிக்கப்படுகிறது. ஒரு server மெதுவாக இருந்தால், அது பொதுவாக இந்த இரண்டில் ஏதோ ஒன்றில் சிக்கலைக் கொண்டிருக்கும், மேலும் அவற்றைச் சரிசெய்யும் முறைகளும் வெவ்வேறானவை.
Static batching-ல் மிக மெதுவான பதிலுக்காக அனைவரும் காத்திருக்க வேண்டியிருக்கும்
Static batching என்பது ஒரு அடிப்படை முறையாகும். நீங்கள் application code-ல் கோரிக்கைகளை (requests) நீங்களாகவே தொகுக்கும்போது இது நிகழ்கிறது. Engine N எண்ணிக்கையிலான கோரிக்கைகளைச் சேகரித்து, அவற்றை ஒன்றாக இயக்குகிறது. அந்தத் தொகுப்பில் உள்ள மிக நீண்ட generation முடியும் வரை, ஒவ்வொரு slot-ம் அப்படியே வைக்கப்பட்டிருக்கும்.
1,200 tokens கொண்ட சுருக்கத்தைக் கேட்கும் ஒரு பயனர், நான்கு ஒரு-வரி பதில்களை அந்தத் தொகுப்பிலேயே முடக்கி வைக்கிறார். ஏனெனில், அந்தத் தொகுப்பில் உள்ள மிக மெதுவான கோரிக்கை முடியும் வரை எந்த slot-ம் விடுவிக்கப்படாது.
இதனால் இரண்டு பாதிப்புகள் ஏற்படுகின்றன. முடிவடைந்த sequences எவ்வித பயனுள்ள கணக்கீடும் செய்யாமல் slot-களை ஆக்கிரமித்துக் கொள்கின்றன. இதனால் output நீளம் மாறும்போது, ஒட்டுமொத்த throughput குறைகிறது; chat output-ன் நீளம் அடிக்கடி மாறுவது வழக்கம். ஒரு தொகுதி உருவான பிறகு வரும் கோரிக்கை, அந்தத் தொகுதி முழுமையாக முடியும் வரை காத்திருக்க வேண்டும். அதன் prefill தொடங்குவதற்கு முன்பே இது நடப்பதால், அந்தப் புதிய கோரிக்கையின் TTFT மற்றவரின் நீண்ட பதிலால் தீர்மானிக்கப்படுகிறது.
Continuous batching ஒவ்வொரு token-க்கும் கோரிக்கைகளை ஏற்றுக்கொண்டு நிறைவு செய்கிறது
Continuous batching என்பது ஒரு decoding படிநிலையின் அளவில் திட்டமிடப்படுகிறது. ஒவ்வொரு படிநிலைக்குப் பிறகும், scheduler தனது stop token-ஐ வெளியிட்ட sequences-ஐ நீக்கிவிட்டு, காலியாக உள்ள இடங்களுக்குக் காத்திருக்கும் கோரிக்கைகளை அனுமதிக்கிறது. 40-வது படிநிலையில் முடிவடையும் ஒரு பதில், batch-ன் முடிவில் அல்லாமல், 40-வது படிநிலையிலேயே தனது இடத்தை விடுவிக்கிறது.
இது ஒன்றும் விசித்திரமானதல்ல. llama-server, -cb, --cont-batching-ஐ "continuous batching-ஐ (dynamic batching என்றும் அழைக்கப்படுகிறது) செயல்படுத்த வேண்டுமா (default: enabled)" என்று ஆவணப்படுத்துகிறது, மேலும் vLLM இந்த அடிப்படையிலேயே கட்டமைக்கப்பட்டுள்ளது. Ollama-வும் இணையாக (parallel) கோரிக்கைகளை வழங்குகிறது. Default அமைப்பில் இதன் எண்ணிக்கை ஒன்றாக மட்டுமே இருப்பதால், பல பயனர்கள் தங்கள் வன்பொருளால் (hardware) concurrency-ஐ கையாள முடியாது என்று தவறாகக் கருதுகின்றனர்; உண்மையில், அவர்களின் configuration தான் அதைத் தடுத்திருக்கும்.
வெளியிடப்பட்ட continuous batching முடிவுகள் பொதுவாக datacenter cards-ல் அளவிடப்படுகின்றன; அவற்றில் கூடுதல் compute வசதியும், cache-க்காகப் பல gigabytes நினைவகமும் இருக்கும். அந்த முடிவுகளின் வடிவம் உங்கள் கணினிக்கும் பொருந்தும். ஆனால், அவற்றின் அளவு பொருந்தாது; அதற்கான காரணம் கீழே உள்ள நினைவகப் பகுதியில் விளக்கப்பட்டுள்ளது.
Prefill மற்றும் decode ஆகியவற்றுக்கு இடையே கணக்கீட்டுத் திறன் போட்டி
நான்கு பதில்கள் streaming ஆகிக்கொண்டிருக்கும்போது ஒரு புதிய கோரிக்கை வந்தால், அதன் prompt முதலில் prefill செய்யப்பட வேண்டும்; prefill அதிக கணக்கீட்டுத் திறனைப் பயன்படுத்தும். scheduler அந்த prefill-க்குத் தனிப்படியை (step) ஒதுக்கினால், அந்த நேரத்தில் streaming-ல் இருக்கும் நான்கு பயனர்களுக்கும் எந்த token-ம் கிடைக்காது. நீண்ட prompt-ஆக இருந்தால், திறந்திருக்கும் அனைத்து விண்டோக்களிலும் இது ஒரு தெளிவான இடைவெளியாகத் தெரியும். வேறொருவர் கோரிக்கையை அனுப்பும்போது server-ல் ஏற்படும் இந்தத் தடுமாற்றத்தையே பயனர்கள் 'hiccups' என்று குறிப்பிடுகின்றனர்.
Chunked prefill, ஒரு நீண்ட prompt-ஐத் துண்டுகளாகப் பிரித்து, ஒவ்வொரு துண்டையும் இயங்கிக்கொண்டிருக்கும் decode-களுடன் ஒரே படியில் கலக்கிறது. vLLM-ன் tuning வழிகாட்டி இதிலுள்ள சமரசத்தை நேரடியாகக் குறிப்பிடுகிறது: சிறிய chunk அளவுகள் "குறைவான prefill-களே decode-களை மெதுவாக்கும் என்பதால் சிறந்த ITL-ஐத் தரும்", அதேசமயம் அதிக மதிப்புகள் "ஒரே தொகுப்பில் அதிக prefill token-களைச் செயலாக்க முடிவதால் சிறந்த time to first token (TTFT)-ஐத் தரும்". நீங்கள் யாருடைய அனுபவத்திற்கு முன்னுரிமை அளிக்கிறீர்கள் என்பதைத் தீர்மானிக்க வேண்டும்: பதிலுக்காகக் காத்திருப்பவரா அல்லது உரை streaming ஆவதைப் பார்ப்பவரா?
Prompt-ன் நீளமே இது எவ்வளவு பாதிப்பை ஏற்படுத்தும் என்பதைத் தீர்மானிக்கிறது. 6,000 token கொண்ட prompt மற்றும் 200 token கொண்ட பதில் எனில், 6,000 token-களுக்கான prefill வேலை 200 decode படிகளுக்கு எதிராக நடக்கிறது. Retrieval-augmented chat மற்றும் நீண்ட system prompt-கள் உங்களை இந்த நிலைக்குத் தள்ளும், எனவே prefill என்பது புறக்கணிக்கக்கூடிய ஒன்றாக இல்லாமல், பயனர்கள் காத்திருக்க வேண்டிய முக்கிய காரணியாக மாறுகிறது. நீண்ட பகுதி மீண்டும் மீண்டும் வரும்போது Prefix caching உதவுகிறது: vLLM --enable-prefix-caching-ஐ வழங்குகிறது, இது ஒவ்வொரு கோரிக்கைக்கும் மீண்டும் கணக்கிடுவதற்குப் பதிலாக, பகிரப்பட்ட prompt prefix-க்கான cache-ஐ மீண்டும் பயன்படுத்துகிறது.
முதலில் தீர்ந்துபோகும் நினைவகம் KV cache ஆகும்
ஒவ்வொரு செயலில் உள்ள உரையாடலிலும் உள்ள ஒவ்வொரு டோக்கனும், மாதிரியின் ஒவ்வொரு அடுக்கிலும் (layer) ஒரு key vector மற்றும் value vector-ஐ விட்டுச் செல்கிறது. இதுவே KV cache (key/value cache) ஆகும். ஒவ்வொரு புதிய டோக்கனுக்கும் முழு prompt-ஐயும் மீண்டும் கணக்கிடுவதைத் தவிர்க்க இது உதவுகிறது. ஒரு டோக்கனுக்கான இதன் அளவு மாதிரியின் அமைப்பால் தீர்மானிக்கப்படுகிறது: 2 (ஒரு key, ஒரு value) பெருக்கல் அடுக்கு எண்ணிக்கை, பெருக்கல் key/value heads எண்ணிக்கை, பெருக்கல் head dimension, பெருக்கல் ஒரு மதிப்பிற்கான பைட்டுகள். இந்த எண்களை மாதிரியின் config.json-லிருந்து கண்டறியவும்.
இதை ஒருமுறை கணக்கிட்டால், நினைவக வரம்பு குறித்த குழப்பம் நீங்கும். 36 அடுக்குகள், 8 key/value heads மற்றும் 128 head dimension கொண்ட ஒரு பொதுவான 8B மாதிரி, 16-bit-ல் cache-ஐ வைத்திருக்கும்போது, ஒரு டோக்கனுக்கு 2 36 8 128 2 பைட்டுகள் தேவைப்படுகிறது. இது 147,456 பைட்டுகள், அதாவது சுமார் 144 KiB. எனவே, 8,192 டோக்கன் கொண்ட ஒரு உரையாடலுக்கு சுமார் 1.2 GB cache தேவைப்படுகிறது. மாதிரியின் எடைகளுக்கு (weights) மேலதிகமாக, ஐந்து உரையாடல்களுக்கு சுமார் 6 GB தேவைப்படும். எத்தனை பயனர்கள் இதில் இயங்க முடியும் என்பதற்கான உண்மையான விடை இதுதான்.
ஒரே நேரத்தில் பல கோரிக்கைகளைச் செயலாக்குவது (Concurrency) context அளவைப் பெருக்குகிறது, இதை கருவிகள் வெளிப்படையாகவே கூறுகின்றன. Ollama-வின் FAQ: "ஒரு குறிப்பிட்ட மாதிரிக்கு ஒரே நேரத்தில் கோரிக்கைகளைச் செயலாக்கும்போது, அந்த கோரிக்கைகளின் எண்ணிக்கைக்கு ஏற்ப context அளவு அதிகரிக்கிறது. உதாரணமாக, 4 கோரிக்கைகள் ஒரே நேரத்தில் நடக்கும்போது, 2K context என்பது 8K context ஆக மாறி கூடுதல் நினைவகத்தை எடுத்துக்கொள்ளும்." தேவையான RAM என்பது OLLAMA_NUM_PARALLEL பெருக்கல் OLLAMA_CONTEXT_LENGTH என்ற அளவில் அமையும். llama-server-ல், -c மூலம் நீங்கள் கேட்கும் context, -np slots-க்கு இடையே பகிர்ந்து அளிக்கப்படுகிறது. எனவே, slot எண்ணிக்கையை மட்டும் அதிகரிப்பது ஒவ்வொரு கோரிக்கையும் வைத்திருக்கக்கூடிய அளவைக் குறைக்கும். தொடக்க லாக் (startup log)-லிருந்து ஒவ்வொரு slot-க்கான context அளவைச் சரிபார்க்கவும், ஊகிக்க வேண்டாம்.
vLLM அதற்குப் பதிலாக முன்கூட்டியே நினைவகத்தை ஒதுக்குகிறது (preallocate). --gpu-memory-utilization (இயல்புநிலை 0.92) என்பது "மாதிரி இயக்கிக்கு (model executor) பயன்படுத்தப்பட வேண்டிய GPU நினைவகத்தின் பங்கு" ஆகும். எடைகளுக்குப் பிறகு எஞ்சியிருக்கும் நினைவகம் paged KV pool-ஆக மாறுகிறது. அந்த pool தீர்ந்துபோகும்போது, scheduler ஒரு கோரிக்கையைத் தோல்வியடையச் செய்வதற்குப் பதிலாக அதை நீக்குகிறது (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 முறை RECOMPUTE ஆகும். எனவே, நீக்கப்பட்ட ஒரு கோரிக்கை அதன் cache-ஐ இழந்து, மீண்டும் அனுமதிக்கப்படும்போது prefills-ஐச் செய்யும். அந்த வேலை இரண்டு முறை செய்யப்படுகிறது. "preemption மற்றும் recomputation ஆகியவை end-to-end latency-ஐப் பாதிக்கலாம்" என்று ஆவணங்கள் எச்சரிக்கின்றன. உங்கள் சராசரி latency சரியாகத் தெரிந்தாலும், ஒரு பயனர் மட்டும் ஏன் நீண்ட நேரம் காத்திருந்தார் என்பதற்கு இந்த லாக் வரியே சிறந்த விளக்கமாகும். ஒட்டுமொத்த எண்ணிக்கையைப் பதிவு செய்ய 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 பாதியாகக் குறையும்; மேலும், மிகக் குறைந்த வளங்களுக்குள் cache தேவை இருமடங்காகும்.
ஐந்து பயனர்கள். இந்த நிலையில் இயல்புநிலை அமைப்புகள் (defaults) போதுமானதாக இருக்காது, இது ஒரு queue பிரச்சினையாக மாறத் தொடங்கும். OLLAMA_NUM_PARALLEL மதிப்பு 1 ஆக இருக்கும்போது, நீண்ட பதிலை எதிர்பார்க்கும் ஒருவருக்காக மற்ற நால்வர் காத்திருக்க வேண்டியிருக்கும், அவர்களின் முறை வரும்போது மட்டுமே அவர்களுக்கு இயல்பான வேகம் கிடைக்கும். Parallel count-ஐ உயர்த்தினால் பிரச்சினையின் தன்மை மாறும்: தலா 8K context கொண்ட ஐந்து slots-க்கு மொத்தம் 40K token cache தேவைப்படும். இது VRAM-ல் பொருந்தவில்லை என்றால், engine சில layers-ஐ system RAM-க்கு மாற்றும்; அதுவும் போதவில்லை என்றால், box swap ஆகத் தொடங்கி tokens per second வேகம் அதலபாதாளத்திற்குச் சரிந்துவிடும்.
இருபது பயனர்கள். ஒரு chat UI-ல் இருக்கும் இருபது நபர்கள் எப்போதும் இருபது concurrent requests-ஐ உருவாக்குவதில்லை; வன்பொருளை (hardware) வாங்குவதற்கு முன் இதைப் புரிந்துகொள்வது மிக முக்கியம். ஒரு பயனர் பதிலை வாசித்துவிட்டு அடுத்த கேள்விக்கு முன் 20 முதல் 60 வினாடிகள் வரை சிந்திப்பார், எனவே அவர்களின் session-ன் பெரும்பகுதி idle-ஆகவே இருக்கும். ஆனால், இருபது agents அல்லது இருபது document summarisation பணிகள் என்பது idle நேரம் இல்லாத இருபது உண்மையான streams ஆகும். அதற்கு முற்றிலும் மாறுபட்ட இயந்திரம் தேவைப்படும்.
உங்கள் பயனர்கள் ஒரே நேரத்தில் செயல்படுபவர்களா அல்லது உள்நுழைந்திருப்பவர்கள் மட்டுமா?
எந்தவொரு அளவீட்டையும் தீர்மானிக்கும் முன், செயல்பாட்டில் உள்ள கோரிக்கைகளை (requests in flight) கணக்கிடுங்கள். இதற்கான கணக்கீடு எளிதானது: செயல்பாட்டில் உள்ள கோரிக்கைகள் என்பது, பயனர்களின் எண்ணிக்கை பெருக்கல் ஒரு முறைக்கு உருவாக்க எடுக்கும் நொடிகள், வகுத்தல் இரண்டு முறைக்கு இடைப்பட்ட நொடிகள்.
- முதலில் உங்கள் சொந்த single-stream வேகத்தை அளவிடுங்கள், prefill மற்றும் decode இரண்டையும் கணக்கில் கொள்ளுங்கள். வேறொருவரின் card-ல் உள்ள எண்ணைப் பயன்படுத்த வேண்டாம்: உங்கள் கணினியில் tokens per second-ஐ அளவிடுங்கள் மற்றும் உங்களுக்குக் கிடைக்கும் முடிவைப் பயன்படுத்துங்கள்.
- Duty cycle-ஐ மதிப்பிடுங்கள். இருபது chat பயனர்கள், ஒரு முறைக்கு 12 நொடிகள் உருவாக்கும் நேரம், ஒவ்வொரு 90 நொடிகளுக்கும் ஒரு முறை எனில், 20 * 12 / 90 என்பது சுமார் 2.7 செயல்பாட்டில் உள்ள கோரிக்கைகளைக் குறிக்கும்.
- Slot எண்ணிக்கையை அதைவிடச் சற்று அதிகமாக அமைத்து, பின் நினைவகத்துடன் (memory) ஒப்பிட்டுப் பாருங்கள்: slots பெருக்கல் ஒரு கோரிக்கைக்கான context, உங்களிடம் உள்ள cache tokens-க்குள் அடங்க வேண்டும்.
- Queue-வைச் சிறியதாக வைத்திருங்கள், அப்போதுதான் overflow ஏற்பட்டால் அது விரைவாகவும் தெளிவாகவும் தெரியும்.
கிடைக்கக்கூடிய cache tokens என்பது weights-க்கு பிந்தைய மீதமுள்ள நினைவகம், அதை மேலே உள்ள பகுதியில் குறிப்பிட்ட per-token செலவால் வகுக்க வேண்டும். 24 GB card-ல் 16-bit-ல் இயங்கும் 8B model, weights-க்காக சுமார் 16 GB-ஐப் பயன்படுத்துகிறது. இயல்புநிலை பயன்பாட்டில் (default utilisation) சுமார் 6 GB cache கிடைக்கிறது, இது ஐந்து 8K உரையாடல்களுக்குச் சமம். கூடுதலாகச் சேர்க்க, per-request context-ஐக் குறைக்கவும் அல்லது cache-ஐ 8-bit-ல் சேமிக்கவும் (llama-server இதற்கு --cache-type-k q8_0 தேவைப்படும்). இவை இரண்டுமே சில சமரசங்களுடன் concurrency-ஐ அதிகரிக்கின்றன. பணத்தை hardware-ல் முதலீடு செய்வதற்கு முன், இந்தச் சமரசங்களைப் புரிந்துகொள்வது அவசியம்: GPU VPS எப்போது API tokens-ஐ விட லாபகரமாகிறது.
Ollama-ன் இயல்புநிலை அமைப்புகள் போதுமானதாக இல்லாத சூழல்
Service unit மூலம் parallel count-ஐ அதிகரிக்கவும். ஏனெனில், 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) திரையில் தெரிய வேண்டும். அவ்வாறு தெரியவில்லை என்றால், drop-in கோப்பு சரியாகச் சேமிக்கப்படவில்லை என்று அர்த்தம்; அதன் பிறகு நீங்கள் செய்யும் எந்த மாற்றமும் பலனளிக்காது. ollama ps கட்டளையானது, தற்போது ஏற்றப்பட்டுள்ள model-ன் அளவை அதன் weights-ஐ விட அதிகமாகக் காட்டும். ஏனெனில், 8,192 tokens கொண்ட நான்கு slots, அவற்றுடன் சேர்த்து 32,768 tokens cache-ஐ ஒதுக்குகின்றன. அனைத்து model-ம் GPU-வில் இருக்கும் என்று நீங்கள் எதிர்பார்த்த நிலையில், PROCESSOR நெடுவரிசையில் model-ன் ஒரு பகுதி CPU-வில் இருப்பதாகக் காட்டினால், GPU-வின் கொள்ளளவை விட அதிக cache-ஐ நீங்கள் கோரியுள்ளீர்கள் என்று பொருள். அந்த இரண்டு எண்களில் ஏதேனும் ஒன்றைக் குறைக்கவும்.
Queue-ன் இயல்புநிலை அமைப்பை மீண்டும் கவனிக்க வேண்டும். Ollama அதிகபட்சமாக OLLAMA_MAX_QUEUE கோரிக்கைகளை வரிசைப்படுத்தும், "இதன் இயல்புநிலை மதிப்பு 512 ஆகும்". இதைத் தாண்டினால், "server அதிக சுமையுடன் உள்ளது என்பதைக் குறிக்கும் 503 error" செய்தியை அது வழங்கும். ஒரே நேரத்தில் நான்கு கோரிக்கைகளை மட்டும் கையாளும் ஒரு server-ல் 512 கோரிக்கைகள் கொண்ட வரிசையை வைத்திருப்பது சாத்தியமற்றது. ஏனெனில், 300-வது இடத்தில் இருக்கும் client-ன் கோரிக்கை, அதன் முறை வருவதற்கு முன்பே காலாவதியாகிவிடும் (timeout). வரிசையின் அளவைச் சிறியதாக வைத்திருப்பது சிறந்தது; அப்போதுதான் உங்கள் application-ஆல் பிழையை உணர்ந்து மீண்டும் முயற்சி செய்யவோ அல்லது பயனருக்குத் தெரிவிக்கவோ முடியும். முடிவில்லாத காத்திருப்பை விட இதுவே சிறந்தது.
இதை நேரடியாகச் சோதிக்கவும். இரண்டு terminal-களிலிருந்து ஒரே நேரத்தில் இரண்டு கோரிக்கைகளை அனுப்பி, இரண்டையும் கவனிக்கவும். முதல் கோரிக்கை முடியும் வரை இரண்டாவது கோரிக்கை எந்தப் பதிலையும் தராமல் இருந்தால், parallel அமைப்பு செயல்படவில்லை என்று அர்த்தம்.
ஒரு உண்மையான serving engine எப்போது லாபகரமாகிறது
உங்களிடம் போதுமான GPU திறன் இருந்து, நான்குக்கும் மேற்பட்ட கோரிக்கைகள் (requests) ஒரே நேரத்தில் செயல்பாட்டில் இருக்கும்போது vLLM-ன் கூடுதல் அமைப்பு முறை பயனுள்ளதாக இருக்கும். இதன் scheduler ஒவ்வொரு token-க்கும் தனித்தனியாகச் செயல்படுகிறது, cache நினைவகம் பக்கங்களாக (paged) பிரிக்கப்படுவதால் காலியாக உள்ள பகுதிகள் மீண்டும் பயன்படுத்தப்படுகின்றன, மேலும் இது பயன்படுத்தப்படாத VRAM-ஐ செயல்திறனாக மாற்றுகிறது. ஆகஸ்ட் 2026 நிலவரப்படி, இதன் நிறுவல் மற்றும் தொடக்கத்திற்கு இரண்டு கட்டளைகள் மட்டுமே தேவை:
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."}]
}'choices array அடங்கிய பதில் கிடைத்தால், server இயங்குகிறது மற்றும் model ஏற்றப்பட்டுள்ளது என்று அர்த்தம். சுமை அதிகமாக இருக்கும்போது, கவனிக்க வேண்டிய இரண்டு முக்கிய அமைப்புகள்: --max-num-seqs, இது "ஒரே iteration-ல் செயலாக்கப்பட வேண்டிய அதிகபட்ச sequences எண்ணிக்கை", மற்றும் --max-num-batched-tokens, இது "ஒரே iteration-ல் செயலாக்கப்பட வேண்டிய அதிகபட்ச tokens எண்ணிக்கை". முதலாவது concurrency-ஐக் கட்டுப்படுத்துகிறது. இரண்டாவது, முன்னரே விவரிக்கப்பட்ட chunked prefill வரம்பாகும்.
நான்குக்கும் குறைவான கோரிக்கைகள் இருக்கும்போதோ அல்லது ஆதரவு இல்லாத GPU உள்ள கணினிகளிலோ vLLM-ஐப் பயன்படுத்துவது தேவையற்ற சிக்கலை மட்டுமே தரும், பலன் கிடைக்காது. இது CUDA-class card-ஐ எதிர்பார்க்கிறது மற்றும் தொடக்கத்திலேயே பெரும்பாலான நினைவகத்தை எடுத்துக்கொள்கிறது; இது 4 முதல் 8 GB VPS-க்கு ஏற்றதல்ல. அத்தகைய சூழலில், சிறிய model, குறைந்த context மற்றும் நீங்கள் கட்டுப்படுத்தும் queue ஆகியவையே சரியான தீர்வாகும். Ollama மற்றும் vLLM ஆகிய serving engines-க்கு இடையிலான வேறுபாடுகள் இந்தத் தேர்வை விரிவாக விளக்குகிறது, மேலும் VPS-ல் Qwen 3 8B-ஐ இயக்குதல் ஒரு நடுத்தர அளவிலான model-க்கு, கூடுதல் பயனர் வருவதற்கு முன்பே எவ்வளவு வன்பொருள் தேவைப்படும் என்பதைக் காட்டுகிறது.
நாட்டுப்புறக் கதைகள் மறைக்கும் வர்த்தக பரிமாற்றம் (Tradeoff)
Continuous batching ஒட்டுமொத்த throughput-ஐ அதிகரிக்கிறது, மேலும் இது பொதுவாக median latency-ஐயும் மேம்படுத்துகிறது, ஏனெனில் வரிசையில் காத்திருக்கும் கோரிக்கை விரைவில் தொடங்குகிறது. Tail latency இதற்கு நேர்மாறாகச் செயல்படுகிறது, ஆனால் அந்தப் பாதி பெரும்பாலும் குறிப்பிடப்படுவதில்லை.
ஒவ்வொரு படியிலும் சேர்க்கப்படும் கூடுதல் sequence சிறிய அளவிலான வேலையைச் சேர்க்கிறது, எனவே batch நிரம்பும்போது அனைவருக்கும் ITL அதிகரிக்கிறது. புதிதாக வரும் ஒரு கோரிக்கையின் prefill, streaming பயனர்களுக்குக் கிடைக்க வேண்டிய படியின் ஒரு பகுதியை எடுத்துக்கொள்கிறது. Cache அழுத்தம் இருக்கும்போது scheduler preempt செய்கிறது, இது பாதியில் உருவாக்கப்பட்ட கோரிக்கையை அதன் prefill-ன் தொடக்கத்திற்கே திருப்பி அனுப்புகிறது.
ஒரு chat UI சராசரிகளை அல்ல, tails-ஐயே காட்டுகிறது. ஒரு வாக்கியத்தின் நடுவில் இரண்டு வினாடிகள் நின்றுவிடும் stream, முழுமையாக முடிவடைவதற்கான மொத்த நேரம் குறைவாக இருந்தாலும், பழுதடைந்ததாகவே கருதப்படுகிறது. நீங்கள் எதிர்பார்க்கும் சுமையின் கீழ் p95 TTFT மற்றும் p95 ITL ஆகியவற்றை அளவிடுங்கள், மேலும் mean tokens per second என்பதை அனுபவத்தின் விளக்கமாக அல்லாமல், ஒரு capacity எண்ணாகக் கருதுங்கள்.
இதிலிருந்து நடைமுறை அமைப்பு உருவாகிறது. நினைவகம் அனுமதிக்கும் அளவை விட சற்றே குறைவாக concurrency-ஐக் கட்டுப்படுத்துங்கள், அப்போதுதான் engine ஒருபோதும் preempt செய்ய வேண்டிய அவசியம் இருக்காது. தத்தளிக்கும் (thrashes) பெரிய batch-ஐ விட, சிறிய மற்றும் கணிக்கக்கூடிய வரிசையே சிறந்தது. ஏனெனில், நான்கு வினாடிகள் காத்திருந்து சீராக stream செய்யும் பயனர், உடனடியாகத் தொடங்கி இரண்டு முறை தடைபடும் பயனரை விட மகிழ்ச்சியாக இருப்பார்.
வேகம் குறைவாக இருக்கும்போது எதைச் சரிபார்க்க வேண்டும்
ஒவ்வொரு பயனரும் சாதாரணமாக உள்ளனர், ஆனால் காத்திருப்பு நேரம் அதிகமாக உள்ளது. இது ஒரு வரிசை (queue) சார்ந்த சிக்கல், வேகப் பிரச்சனை அல்ல. முதலில் parallel அமைப்பைச் சரிபார்க்கவும். மாதிரி (model) சரியாகச் செயல்படுகிறது, ஆனால் ஒரு நேரத்தில் ஒரு கோரிக்கையை மட்டுமே கையாள்கிறது.
Ollama-விலிருந்து HTTP 503 பிழை. வரிசை நிரம்பிவிட்டது. சர்வர் அதன் முழுத் திறனில் இயங்குகிறது அல்லது சுமையைக் குறைப்பதற்காக OLLAMA_MAX_QUEUE வேண்டுமென்றே குறைவாக அமைக்கப்பட்டுள்ளது; இது நீங்கள் எதிர்பார்க்கும் செயல்பாடே ஆகும்.
CPU சர்வரில் சுமை அதிகரிக்கும்போது ஒரு வினாடிக்கான டோக்கன்களின் (tokens per second) எண்ணிக்கை குறைகிறது. இது நடக்கும்போது vmstat 1 கட்டளையை இயக்கவும். si மற்றும் so நெடுவரிசைகளில் பூஜ்ஜியமற்ற மதிப்புகள் இருந்தால், கணினி swapping செய்கிறது என்று அர்த்தம்; அதாவது ஒவ்வொரு டோக்கனுக்கும் வட்டில் (disk) இருந்து எடைகள் (weights) படிக்கப்படுகின்றன. எந்தவொரு உள்ளமைவு மாற்றமும் இதைச் சரிசெய்யாது. மாதிரியின் அளவைக் குறைக்கவும் அல்லது slot எண்ணிக்கையைக் குறைக்கவும்.
பத்து பயனர்களில் ஒருவர் மற்றவர்களை விட நீண்ட நேரம் காத்திருக்கிறார். vLLM பதிவுகளில் (logs) preempted என்பதைத் தேடவும். Preemption மற்றும் அதன் மறு கணக்கீடு (recompute) இதற்கு முக்கிய காரணமாகும்; நீங்கள் அனுமதிக்கும் context நீளத்திற்கு cache போதுமானதாக இல்லை என்று இது குறிக்கிறது.
சர்வர் சும்மா இருக்கும்போதும் TTFT மோசமாக உள்ளது. இது concurrency அல்ல, prefill சிக்கல். நீண்ட prompt-கள் முதல் டோக்கன் தோன்றுவதற்கு முன்பே அதிக நேரத்தை எடுத்துக்கொள்ளும், எனவே வன்பொருளை (hardware) ஆராய்வதற்கு முன் prompt அளவு மற்றும் prefix caching ஆகியவற்றைச் சரிபார்க்கவும்.
FAQ
எனது self-hosted LLM-ஐ இரண்டாவது நபர் பயன்படுத்தும்போது ஏன் வேகம் குறைகிறது?
பெரும்பாலான நேரங்களில் வேகம் குறைவதில்லை, மாறாக அது வரிசையில் (queue) காத்திருக்கிறது. Ollama OLLAMA_NUM_PARALLEL-ஐ 1 என அமைத்து வருவதால், முதல் கோரிக்கை அதன் இறுதி token-ஐ வெளியிடும் வரை இரண்டாவது கோரிக்கை காத்திருக்கும். ஒரு பயனர் stream செய்யும்போது மற்றவர் காத்திருந்தால், அதை வைத்து இரண்டையும் வேறுபடுத்தி அறியலாம்: அவர்கள் stream செய்யத் தொடங்கியதும் tokens per second வேகம் சாதாரணமாக இருந்தால், அது queue பிரச்சினை; parallel count-ஐ அதிகரிப்பதன் மூலம் இதைச் சரிசெய்யலாம். இரண்டு stream-களும் பாதி வேகத்தில் இயங்கினால், நீங்கள் memory bandwidth-ஐப் பகிர்ந்து கொள்கிறீர்கள் என்று அர்த்தம்; இது வன்பொருள் சார்ந்த வரம்பு.
ஒரு சிறிய GPU எத்தனை concurrent பயனர்களுக்கு சேவை வழங்க முடியும்?
பயனர்களைக் கணக்கிடாதீர்கள், memory-ஐக் கணக்கிடுங்கள். முதலில் model weights, பிறகு KV cache. இது ஒரு active conversation-க்கு, ஒரு token-க்கு: 2 layers key/value heads head dimension bytes என்ற அளவில் செலவாகும். 36 layers, 8 key/value heads மற்றும் 128 head dimension கொண்ட ஒரு 8B model, 16-bit-ல் ஒரு token-க்கு சுமார் 144 KiB எடுத்துக்கொள்ளும். எனவே, 8,192 token கொண்ட ஒரு conversation-க்கு சுமார் 1.2 GB தேவைப்படும். 24 GB card-ல் அந்த model-ஐ 16-bit-ல் வைத்தால், cache-க்கு சுமார் 6 GB மீதமிருக்கும். இது முழு context-ல் சுமார் ஐந்து conversations-க்கு போதுமானது; context-ஐக் குறைத்தால் இன்னும் பலவற்றைச் சேர்க்கலாம்.
Continuous batching ஒவ்வொரு பயனரின் பதிலையும் மெதுவாக்குகிறதா?
பொதுவாக median latency மேம்படும், ஏனெனில் ஒரு முழு batch முடியும் வரை கோரிக்கைகள் காத்திருக்க வேண்டியதில்லை. ஆனால், tail latency மோசமடையும். ஒவ்வொரு கூடுதல் sequence-ம் decoding-ன் ஒவ்வொரு நிலையிலும் கூடுதல் வேலையைச் சேர்க்கிறது. புதிதாக வரும் கோரிக்கையின் prefill, streaming பயனர்களின் ஒரு பகுதியை எடுத்துக்கொள்கிறது, மேலும் preempt செய்யப்பட்ட கோரிக்கை இரண்டு முறை prefill செய்யப்பட வேண்டும். சராசரியை (average) பார்க்காமல், p95 inter-token latency-ஐ அளவிடுங்கள்; ஏனெனில் chat window-ல் ஏற்படும் தாமதங்களை சராசரி மறைத்துவிடும்.
நான் OLLAMA_NUM_PARALLEL-ஐ அதிகரிக்க வேண்டுமா அல்லது vLLM-க்கு மாற வேண்டுமா?
முதலில் parallel count-ஐ அதிகரிக்கவும். இது இலவசம் மற்றும் ஒரு drop-in file மூலம் செய்துவிடலாம். ஒரு நீண்ட பதிலுக்காக நான்கு பேர் வரிசையில் காத்திருக்கும் பொதுவான பிரச்சினைக்கு இது தீர்வாகும். Memory தான் இங்கு வரம்பு: parallel கோரிக்கைகள் நீங்கள் வைத்திருக்க வேண்டிய context-ஐப் பெருக்குகின்றன, எனவே layers CPU-க்கு மாறுகிறதா என்பதைக் கவனியுங்கள். உங்களிடம் போதுமான VRAM இருந்து, நான்குக்கும் மேற்பட்ட கோரிக்கைகள் ஒரே நேரத்தில் இயங்கினால் vLLM-க்கு மாறவும். ஏனெனில், அந்த நிலையில் தான் paged cache மற்றும் per-token scheduling ஆகியவை அதன் செலவை விட அதிக பலனைத் தரும்.
அதிக CPU cores LLM server-ன் வேகத்தை அதிகரிக்குமா?
பயனர்கள் கவனிக்கும் முக்கிய பகுதியில் இது பெரிய மாற்றத்தை ஏற்படுத்தாது. Decode செய்யும்போது ஒவ்வொரு token-க்கும் முழு model-ம் memory-லிருந்து வாசிக்கப்படுகிறது, எனவே இது RAM bandwidth-ஐச் சார்ந்தது. Bandwidth முழுமையாகப் பயன்படுத்தப்படும்போது, கூடுதல் cores எந்த உதவியும் செய்யாது. Prefill செயல்முறை cores-க்கு ஏற்ப அதிகரிக்கும், எனவே நீண்ட prompts-க்கு முதல் token கிடைப்பதற்கான நேரத்தை இது குறைக்கும். 4 முதல் 8 GB VPS-ல், memory capacity தான் முக்கிய தடையாக இருக்கும். எனவே, அதிக vCPU-களை விட, சிறிய model-ஐப் பயன்படுத்துவது அல்லது context-ஐக் குறைப்பது சிறந்த தீர்வாகும்.