Paano sukatin ang tokens per second ng local LLM
Alamin ang tunay na tokens per second gamit ang concurrency sweep. Ihambing ang throughput sa per-token billing para malaman kung sulit ang rented GPU.
Bakit tinutukoy ng tokens per second kung sulit ang GPU
Ang tokens per second ay ang bilis kung gaano karaming output text ang ginagawa ng server. Ito ang numerong tumutukoy kung mas mura ang pag-rent ng GPU kaysa sa pagbabayad sa API batay sa token. Sinisingil ang GPU box kada oras, ginagamit man ito o idle. Ang hosted API naman ay sinisingil batay sa token. Kaya lamang mas makatitipid ang GPU kung sapat ang output rate sa malaking bahagi ng mga oras na binabayaran mo.
Ibig sabihin, kailangan mo ng aktuwal na sukat, hindi ng numerong nabasa mo lang. Tinutukoy ng page na ito ang apat na numerong mahalagang itala. Ipinapakita rin nito ang mga command na gumagawa ng mga sukat na iyon at ang arithmetic na ginagamit para maging desisyon ang mga ito.
Bakit hindi ang iyong bilang ang published na tokens per second
Nag-publish ang DigitalOcean ng throughput figures noong July 2026 para sa isang NVIDIA H200 na nagpapatakbo ng llama3.3-70b-instruct sa FP8 (8-bit floating point) gamit ang vLLM. Kapaki-pakinabang ang mga figure na ito, pero hindi ang mga ito ang sa iyo.
The data behind this chart
[
{
"config": "H200, one stream",
"tok_s": "47"
},
{
"config": "H100, saturated",
"tok_s": "236"
},
{
"config": "H200, saturated",
"tok_s": "2,036"
},
{
"config": "H200, saturated, in+out",
"tok_s": "4,071.6"
}
]Ang bawat row sa itaas ay kinuha mula sa page na iyon. Dalawa sa mga ito ang mababang dulo ng range na ibinigay nito, kaya ituring ang dalawang iyon bilang floor. Walang figure sa chart na ito ang sinukat namin.
Magsimula sa huling dalawang row. Ang headline ay 4,071.6 tok/s, samantalang ang output-only rate ay 2,036 tok/s. Pinagsasama ng headline ang input at output tokens. Gumamit ang test na iyon ng 1,024 input tokens at 1,024 output tokens, kaya halos kalahati ng headline ang output. Mahalaga ang paghahating ito dahil ang output ang bahagi na sinisingil sa iyo, at ito rin ang mas mabagal na bahagi. Sa prefill (pagbasa ng prompt), pinoproseso ang lahat ng input tokens sa isang pass. Sa decode (pagsulat ng sagot), isang token lang ang ginagawa sa bawat pagkakataon. Pinag-a-average ng total-throughput headline ang murang bahagi at mamahaling bahagi.
Ngayon, tingnan ang unang row. Ang parehong H200, kapag isang request lang ang pinoproseso bawat pagkakataon, ay gumagawa ng 47 tok/s. Ibig sabihin, mahigit apatnapung beses na mas mataas ang saturated figure sa parehong hardware. Nangyayari ang agwat na ito dahil karamihan ng oras ay naghihintay ang GPU sa memory sa isang decode step. Pinupunan ng concurrent requests ang idle time na iyon. Ang ikalawang row, 236 tok/s, ay mula sa isang H100 na nagpapatakbo ng parehong model. Nalilimitahan ito ng KV cache (ang key at value cache, o per-request memory na pinananatili ng isang served conversation sa card). Mas kaunting concurrent requests para sa isang 70B model ang kasya sa 80 GB card, kaya mas mababa ang saturation nito.
Kapag binago mo ang model o ang ratio ng input sa output, magbabago ang bawat bilang sa itaas. Ang published figures ay nagtatakda ng expectations mo, hindi ng budget mo. Ito rin ang parehong patakaran sa tapat na pag-benchmark ng VPS batay sa disk at network.
Apat na numerong mahalaga
- Time to first token, TTFT. Ito ang delay mula sa pagpapadala ng request hanggang sa pagdating ng unang output token. Binubuo ito ng prefill time at queue time. Direktang nararamdaman ito ng user.
- Output tokens per second, bawat stream. Ito ang bilis ng pagsulat ng isang sagot kapag nagsimula na ito. Kapag lampas sa humigit-kumulang 20 tok/s, mas mabilis na ito kaysa sa bilis ng pagbasa ng karamihan sa mga tao, kaya kaunti na lang ang naidudulot ng dagdag na bilis.
- Saturated total output throughput. Ito ang kabuuan ng output sa lahat ng concurrent stream habang fully loaded ang server. Ito ang sukatan ng capacity, at ito ang direktang sumasagot sa GPU bill.
- p50 at p99 TTFT under concurrency. Ang p50 ang nasa gitna ng mga request. Ang p99 ang value na mas mababa rito ang 99 sa bawat 100 request. Laging unang lumilitaw ang epekto ng queueing sa p99.
Gumaganda ang unang dalawang sukatan kapag kaunti ang load ng server. Gumaganda naman ang pangatlo kapag mataas ang load ng server. Magkabilang direksiyon ang hinihila ng mga ito, kaya walang iisang numero na makapaglalarawan sa serving box.
Ayusin muna ang haba ng input at output bago magsukat
Nakadepende ang throughput sa anyo ng traffic. Ang prompt na may 4,000 token at sagot na may 50 token ay prefill-heavy na trabaho. Ang prompt na may 200 token at sagot na may 2,000 token ay decode-heavy na trabaho. Magkaiba nang malaki ang tokens per second na iuulat ng parehong server para sa dalawang ito. Kaya pumili ng isang ratio, ilagay ito sa tabi ng bawat numerong ire-record, at huwag kailanman maghambing ng magkaibang ratio. Makatuwirang default ang 1,024 input at 1,024 output dahil may ilang vendor na naglalathala ng resulta gamit ang ratio na ito. Kung alam mo ang aktuwal mong traffic, gamitin ang aktuwal mong traffic.
Itakda rin ang haba ng output. Kapag umabot ang model sa stop token nito pagkatapos ng 60 token, mas maikli ang run at magmumukhang mas mabilis dahil mas malaking bahagi nito ang TTFT. Tinitiyak ng --ignore-eos flag sa vLLM benchmark client na eksaktong bilang na hinihingi ang i-generate ng bawat request, kaya nananatiling maihahambing ang dalawang run. Mas malaki ang epekto ng pagpili ng model sa mga numerong ito kaysa sa anumang flag: ipinapaliwanag sa pagpapatakbo ng Qwen 3 model sa iisang VPS GPU ang bahagi tungkol sa memorya ng pagpiling iyon.
Sukatin muna ang isang stream
Magsimula sa pinakasimpleng kaso. Sanity check ito at nagsisilbi rin itong ceiling. Ang sarili nitong timing ang ipinapakita ng Ollama.
ollama run llama3.1:8b --verbose "Write 200 words about disk scheduling."Ang linyang dapat basahin ay eval rate, na tumutukoy sa output tokens per second. Ang prompt eval rate ay ang prefill rate, at ang load duration ay ang oras na ginugol sa pag-load ng model sa VRAM. Sa unang call pagkatapos ng cold start, malaki ang load duration, kaya nakalilinlang ang total duration. Patakbuhin ang command nang dalawang beses at basahin ang ikalawang resulta. Awtomatikong inaalis ng Ollama ang idle model pagkalipas ng limang minuto bilang default, kaya kapag mahaba ang pagitan ng mga run, babalik ka sa cold case.
Makukuha rin ang parehong field mula sa API, na mas madaling i-script.
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "Write 200 words about disk scheduling.",
"stream": false
}' | jq '{eval_count, eval_duration,
tok_s: (.eval_count / (.eval_duration / 1000000000))}'Nasa nanoseconds ang eval_duration, kaya ang paghahati rito sa 1,000,000,000 ay nagbibigay ng seconds. Ito mismo ang division na inirerekomenda ng sariling API documentation ng Ollama para sa tokens per second. Kung hindi pa tumatakbo ang server, inilalarawan sa pagho-host ng LLM gamit ang Ollama sa isang VPS ang pag-install at ang systemd unit.
Kailangan ng streaming request para masukat ang TTFT, at maaaring gawin ito ng curl para sa iyo.
curl -s -o /dev/null -N \
-w 'ttfb=%{time_starttransfer}s total=%{time_total}s\n' \
http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"Qwen/Qwen3-8B",
"messages":[{"role":"user","content":"Write 200 words about disk scheduling."}],
"stream":true,"max_tokens":256}'Ang time_starttransfer ay ang sandali kung kailan dumating ang unang byte ng response body. Sa isang streaming chat completion, kabilang ang byte na iyon sa unang server-sent event, na maaaring unang content token o role-only delta na ipinadala bago nito. Kaya ituring ang value bilang TTFT na may maliit na diperensya na isang event. Sapat ang accuracy nito para ikumpara ang dalawang run sa parehong server.
Dalawang beses nakapagpapaganda sa resulta ang single-stream numbers. Pinakamaganda kailanman ang TTFT dahil walang request na nakapila bago sa iyo. Pinakamaganda rin kailanman ang per-stream rate dahil isang request lang ang pinagsisilbihan ng buong card. Hindi ipinapakita ng alinman dito kung gaano karaming load ang kayang dalhin ng server.
Paano magsagawa ng concurrency sweep?
Sa isang sweep, nagpapatakbo ng iisang fixed workload sa pataas na concurrency at itinatala ang resulta sa bawat hakbang. Kasama sa vLLM ang client para rito. Gumagamit ito ng OpenAI API, kaya gumagana rin ito sa Ollama at sa iba pang OpenAI-compatible na system.
vllm bench serve \
--backend openai-chat \
--base-url http://127.0.0.1:8000 \
--endpoint /v1/chat/completions \
--model Qwen/Qwen3-8B \
--dataset-name random \
--random-input-len 1024 \
--random-output-len 1024 \
--ignore-eos \
--num-prompts 160 \
--max-concurrency 16 \
--percentile-metrics ttft,tpot,itl,e2el \
--metric-percentiles 50,99Nililimitahan ng --max-concurrency ang mga request na kasalukuyang tumatakbo, at ito ang variable na sinusweep. Ang --num-prompts ang kabuuang bilang ng mga ipinadalang request, kaya panatilihin itong malapit sa sampung beses ng concurrency para makakuha ng stable na average. Ipinapakita sa summary ang Output token throughput (tok/s): at Total token throughput (tok/s):, kasunod ang Mean TTFT (ms):, Median TTFT (ms):, at P99 TTFT (ms): sa ilalim ng heading na Time to First Token.
Walang per-stream rate sa output na iyon, pero isang division lang ang kailangan para makuha ito. Ang Mean TPOT (ms): ay ang mean time bawat output token pagkatapos ng unang token, kaya ang 25 ms bawat token ay katumbas ng 40 tokens bawat segundo sa bawat stream. Pareho ang resultang makukuha kung hahatiin ang output throughput sa concurrency.
Pagkatapos, i-loop ito at i-save ang bawat run.
for C in 1 4 16 32 64 128; do
vllm bench serve \
--backend openai-chat \
--base-url http://127.0.0.1:8000 \
--endpoint /v1/chat/completions \
--model Qwen/Qwen3-8B \
--dataset-name random \
--random-input-len 1024 \
--random-output-len 1024 \
--ignore-eos \
--num-prompts $(( C * 10 )) \
--max-concurrency "$C" \
--percentile-metrics ttft,tpot,itl,e2el \
--metric-percentiles 50,99 \
--save-result --result-filename "sweep-c$C.json"
donePagbasa sa mga naka-save na JSON file
Isang file ang sinusulat ng bawat run. Kaya kunin nang sabay-sabay ang mga field na kailangan mo mula sa lahat ng file.
for f in sweep-c*.json; do
jq -r --arg f "$f" \
'[$f, .output_throughput, .total_token_throughput,
.median_ttft_ms, .p99_ttft_ms] | @tsv' "$f"
doneAng output_throughput ay output tokens bawat segundo. Idinadagdag ng total_token_throughput ang input tokens, kaya sa 1:1 ratio ay halos doble ito. Umiiral lamang ang p99_ttft_ms dahil isinama ng --metric-percentiles ang 99. Kapag humingi ka ng percentile na hindi mo nirequest, magpi-print ang jq ng null.
Ano ba talaga ang ipinapakita ng concurrency sweep?
The data behind this chart
[
{
"label": "1 stream",
"per_stream_tok_s": 92,
"total_tok_s": 92,
"ttft_p50_ms": 48,
"ttft_p99_ms": 61
},
{
"label": "4 streams",
"per_stream_tok_s": 88,
"total_tok_s": 352,
"ttft_p50_ms": 71,
"ttft_p99_ms": 96
},
{
"label": "16 streams",
"per_stream_tok_s": 71,
"total_tok_s": 1136,
"ttft_p50_ms": 152,
"ttft_p99_ms": 244
},
{
"label": "32 streams",
"per_stream_tok_s": 54,
"total_tok_s": 1728,
"ttft_p50_ms": 287,
"ttft_p99_ms": 498
},
{
"label": "64 streams",
"per_stream_tok_s": 34,
"total_tok_s": 2176,
"ttft_p50_ms": 611,
"ttft_p99_ms": 1240
},
{
"label": "128 streams",
"per_stream_tok_s": 18,
"total_tok_s": 2304,
"ttft_p50_ms": 1490,
"ttft_p99_ms": 3820
}
]Ang 6 row na iyon ay ilustrasyon ng hugis ng isang sweep sa isang maliit na rentable GPU box, batay sa kapani-paniwalang order of magnitude. Hindi iyon sukat ng server mo at hindi rin iyon vendor figure. Patakbuhin ang loop sa itaas at palitan ang mga ito ng sarili mong resulta.
Basahin ang hugis, dahil ang hugis ang nagagamit sa iba pang sitwasyon. Sa isang stream, ang buong box ay nagpo-produce ng 92 tokens bawat segundo, na may p99 TTFT na 61 ms. Sa 128 streams, umaabot ang total sa 2304 tokens bawat segundo, dalawampu't limang beses na mas mataas, habang bumababa ang bilis ng bawat indibidwal na stream sa 18 tokens bawat segundo at umaabot ang p99 TTFT sa 3820 ms. Tumataas ang total throughput dahil ginagawa ng batching na kapaki-pakinabang na trabaho ang dati'y idle na memory waits. Bumababa ang bilis ng bawat stream dahil pinaghahatian na ng mga ito ang parehong compute.
Ang huling pagdodoble ang mahalagang palatandaan. Mula 64 hanggang 128 streams, wala pang anim na porsiyento ang nadadagdag sa total, habang humigit-kumulang tatlong beses naman ang itinatagal ng p99 TTFT. Ibig sabihin, puno na ang KV cache at nakapila na ang mga request sa halip na tumatakbo. Mas maaga ang kapaki-pakinabang na operating point: sa 32 streams, nakakapagbalik pa ang box ng 1728 tokens bawat segundo, o 75 porsiyento ng peak nito, sa bilis na 54 tokens bawat segundo bawat stream at p99 TTFT na 498 ms. I-report ang puntong iyon bilang capacity mo. Ang peak ng curve ay hindi numerong maaari mong gamitin para maghatid ng serbisyo sa mga user.
Hindi pareho ang sinusukat ng Ollama at vLLM
Patakbuhin ang sweep na iyon laban sa isang default na Ollama server, at bahagya lamang magbabago ang total. Naka-default sa 1 ang OLLAMA_NUM_PARALLEL, kaya isang request lang ang tumatakbo habang naghihintay ang iba. Ang queue ang nagpapataas sa p99 TTFT habang nananatiling halos pareho ang total output. Itaas muna ito bago magsukat.
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_NUM_PARALLEL=8"Mag-restart gamit ang sudo systemctl restart ollama, pagkatapos ay tiyaking kasya pa rin ang model. Bawat parallel slot ay may sariling bahagi ng context window, kaya ayon sa documentation ng Ollama, ang 2K context na may 4 parallel request ay naglalaan ng 8K. Kapag masyado mong tinaasan ang bilang ng slot, lalampas ang model sa kapasidad ng VRAM. Suriin ang ollama ps: kung ang column na PROCESSOR ay may value na gaya ng 48%/52% CPU/GPU, nangangahulugang bahagi ng model ay nasa CPU. Bababa ngayon ang throughput habang nagdaragdag ka ng concurrency, sa halip na tumaas. Kapag puno na ang parallel slots, pumipila ang mga request hanggang sa OLLAMA_MAX_QUEUE, na 512 bilang default. Pagkatapos nito, magbabalik ang server ng 503.
Gumagamit ang vLLM ng continuous batching. Tumatanggap ito ng mga bagong request sa kasalukuyang batch kapag may nabakanteng slot, kaya patuloy na tumataas ang curve hanggang maubos ang KV cache. Ino-optimize ang Ollama para sa isang model, isang machine, at mababang setup cost. Kaya magkaibang sagot ang ibinibigay ng dalawang engine sa parehong sweep. Iyan ang tunay na paksa ng Paghahambing ng Ollama at vLLM bilang serving engine. Itala kung aling engine at aling version ang gumawa ng bawat number.
Limang paraan para masukat ang maling bagay
- Malayo ang client. Kapag nag-benchmark mula sa laptop sa internet, nadaragdag ang round trip ng koneksyon mo sa bawat TTFT, kaya home connection mo ang aktuwal na nasusukat. Patakbuhin ang client sa parehong region ng server.
- Cold pa ang model. Binabayaran ng unang request ang pag-load ng weights, at sa vLLM maaari ring kasama rito ang graph capture. Magpadala ng warmup batch at itapon ang resulta.
- Prefix caching ang sumagot para sa iyo. Awtomatikong naka-enable ang prefix caching sa vLLM bilang default, kaya kapag paulit-ulit na ipinapadala ang parehong prompt, cache ang nasusukat sa halip na prefill, at bumababa ang TTFT sa maliit na bahagi ng totoong value. Iniiwasan ito ng
--dataset-name randomdahil magkakaiba ang bawat prompt. Para makatiyak, simulan ang server gamit ang--no-enable-prefix-caching. - Maikli ang outputs. Kapag 32-token ang mga sagot, nangingibabaw ang TTFT sa bawat request at prefill talaga ang inilalarawan ng tokens per second mo. Gamitin ang
--ignore-eosna may makatotohanang haba ng output. - Concurrency 1 ang iniulat mo. Ito ang pinakamagandang tingnan sa sheet, pero wala itong kaugnayan sa cost.
Gawing desisyon ang nasukat na numero
Gamitin ang saturated output throughput mula sa iyong sweep, hindi ang single-stream rate, at ihambing ito sa per-token pricing. Isang division ang break-even:
break_even_tok_s = (price_per_hour / price_per_million_output_tokens) * 1000000 / 3600Gawin natin ito gamit ang mga presyo ng DigitalOcean noong July 2026. Ang dedicated H200 inference endpoint nila ay $4.47 bawat oras, habang ang katumbas na serverless service ay $0.65 bawat milyong token. Kaya ang 4.47 na hinati sa 0.65 ay 6.88 milyong token bawat oras. Kapag hinati ito sa 3,600 segundo, lalabas ang humigit-kumulang 1,910 output token bawat segundo. Kanila ang mga presyo. Atin ang division.
Ang salitang nagpapasya rito ay sustained. Ang pag-abot sa 1,910 token bawat segundo kapag saturated sa loob ng dalawang oras bawat araw ay hindi nangangahulugang 1,910 token bawat segundo ang sustained rate, dahil binabayaran mo rin ang natitirang dalawampu't dalawang oras. Ang sariling crossover calculation ng DigitalOcean para sa mas murang $3.44 bawat oras na GPU Droplet ay nasa 72.2 percent sustained average utilisation. Mas mababa rito, mas sulit ang per-token price. Karaniwang idle GPU hours, hindi mabagal na token, ang nagpapamahal sa self-hosting.
Dalawa ang input sa iyong desisyon. Ibinibigay ng sweep ang ceiling. Ibinibigay ng traffic pattern mo ang fraction ng ceiling na aktuwal mong nakokolekta. I-multiply ang mga ito, pagkatapos ay gamitin ang resulta sa break-even ng GPU VPS laban sa per-token API at tingnan ang sagot para sa iyong volume.
FAQ
Ano ang magandang tokens per second para sa self-hosted LLM?
May dalawang sagot dahil dalawang gamit ang metric na ito. Para sa isang taong nagbabasa ng output, sapat na ang humigit-kumulang 20 output tokens per second bawat stream dahil mas mabilis na ito kaysa sa bilis ng pagbabasa. Hindi na nakatutulong ang mas mataas pa rito. Para sa gastos, ang mahalaga ay ang saturated total output throughput, at ang ibig sabihin ng “maganda” ay ang halagang lumalampas sa break-even point. Kung ikukumpara sa $0.65 bawat milyong token at sa isang box na nagkakahalaga ng $4.47 bawat oras, nasa humigit-kumulang 1,910 sustained output tokens per second ang break-even floor sa mga presyo noong July 2026. Hindi ito naaabot ng isang stream sa malaking model, kaya ginagamit ang batching.
Bakit hindi nagbabago ang Ollama throughput ko kapag nagdagdag ako ng concurrent requests?
OLLAMA_NUM_PARALLEL ay may default na 1, kaya isang request lang bawat model ang pinoproseso ng server at naka-queue ang iba, hanggang OLLAMA_MAX_QUEUE (512 bilang default), bago magbalik ng 503. Hindi nagbabago ang kabuuang output habang tumataas ang p99 TTFT. Palatandaan ito ng queue, hindi ng abalang GPU. Itakda ang variable sa isang systemd drop-in at i-restart ang service. Pagkatapos, tingnan ang ollama ps dahil bawat parallel slot ay nagpaparami sa inilaang context at maaaring magtulak sa bahagi ng model papunta sa CPU.
Dapat ko bang sukatin ang time to first token o tokens per second?
Pareho, dahil magkasalungat ang galaw ng mga ito habang tumataas ang load. Ang TTFT ang nararamdaman ng user, samantalang ang saturated output throughput ang nakikita sa invoice. Itala ang p50 at p99 TTFT sa bawat concurrency step. Pagkatapos, piliin ang pinakamataas na concurrency kung saan katanggap-tanggap pa rin sa iyo ang p99 TTFT. Iulat ang throughput sa puntong iyon bilang iyong capacity, hindi ang maximum na nasa pinakatuktok ng curve.
Palaging nangangahulugan ba ang mas mataas na tokens per second na mas mababa ang cost per token?
Hindi. Ang cost per token ay hourly price na hinati sa dami ng token na aktuwal na nalikha ng box sa loob ng isang oras. Kaya kahit mabilis ang server, mataas pa rin ang cost per token kung halos buong araw itong idle. Utilisation ang nagpapasya rito, hindi ang peak speed. Suriin din ang units: ang binabanggit na total token throughput ay kabilang ang input tokens. Kaya sa 1:1 na input-to-output ratio, halos doble ito ng output rate na pinagbabasehan ng billing.