Como medir tokens por segundo em um LLM local
Aprenda a medir tokens por segundo com um teste de concorrência e descubra o throughput mínimo em que alugar uma GPU supera pagar por token.
Por que os tokens por segundo determinam se uma GPU compensa
Tokens por segundo é a taxa a que o seu servidor produz texto de saída. É também o número que determina se alugar uma GPU fica mais barato do que pagar a uma API por token. Uma máquina com GPU é faturada à hora, esteja ocupada ou inativa. Uma API alojada é faturada por token. Por isso, a GPU só compensa se mantiver uma taxa de saída suficientemente alta durante a maior parte das horas pagas.
Isto significa que precisa de uma medição, não de um valor lido noutro lugar. Esta página define os quatro números que vale a pena registar. Em seguida, apresenta os comandos que os produzem e os cálculos que os transformam numa decisão.
Por que o valor publicado de tokens por segundo não é o seu valor
A DigitalOcean publicou, em julho de 2026, valores de throughput para uma única NVIDIA H200 executando llama3.3-70b-instruct em FP8 (ponto flutuante de 8 bits) no vLLM. Esses valores são úteis, mas não são os seus.
Os dados por trás deste gráfico
[
{
"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"
}
]Todas as linhas acima foram extraídas dessa página, e duas delas correspondem ao limite inferior de um intervalo apresentado na fonte. Portanto, interprete essas duas linhas como um valor mínimo. Nenhum valor deste gráfico foi medido por nós.
Comece pelas duas últimas linhas. O valor de destaque é 4,071.6 tok/s, enquanto a taxa considerando apenas a saída é 2,036 tok/s. O valor de destaque contabiliza os tokens de entrada e de saída em conjunto. Esse teste usou 1,024 tokens de entrada e 1,024 tokens de saída, portanto quase metade do valor de destaque corresponde à saída. Essa divisão é importante porque a saída é a parte mais lenta. O prefill, que lê o prompt, processa todos os tokens de entrada numa única passagem. O decode, que escreve a resposta, produz um token de cada vez. Um valor de throughput total faz a média entre uma operação barata e uma operação cara.
Agora, observe a primeira linha. A mesma H200, servindo uma solicitação de cada vez, produz 47 tok/s. Portanto, o valor com saturação é mais de quarenta vezes superior no mesmo hardware. Essa diferença existe porque uma etapa única de decode deixa a GPU à espera da memória durante a maior parte do tempo, enquanto as solicitações simultâneas ocupam esse tempo ocioso. A segunda linha, 236 tok/s, corresponde a uma única H100 com o mesmo modelo, limitada pelo cache KV (o cache de chave e valor, ou key e value cache, que contém na placa a memória por solicitação mantida por uma conversa em atendimento). Uma placa de 80 GB comporta menos solicitações simultâneas para um modelo de 70B, portanto atinge a saturação com um valor inferior.
Se mudar o modelo ou a proporção entre entrada e saída, todos os valores acima serão alterados. Os valores publicados definem as suas expectativas, não o seu orçamento. A mesma regra aplica-se a avaliar honestamente um VPS quanto ao armazenamento e à rede.
Os quatro números que importam
- Tempo até ao primeiro token, TTFT. O atraso entre o envio de um pedido e a chegada do primeiro token de saída. É o tempo de prefill mais o tempo em fila. O utilizador sente este atraso diretamente.
- Tokens de saída por segundo, por stream. A velocidade a que uma resposta é escrita depois de começar. Acima de aproximadamente 20 tok/s, já é mais rápido do que a maioria das pessoas lê, por isso uma velocidade adicional traz poucos benefícios.
- Throughput total de saída com saturação. A soma de todos os streams concorrentes quando o servidor está totalmente carregado. Este é o valor da capacidade e é o que paga a GPU.
- p50 e p99 de TTFT sob concorrência. p50 é o pedido mediano. p99 é o valor abaixo do qual ficam 99 em cada 100 pedidos. O enfileiramento aparece primeiro no p99.
Os dois primeiros melhoram quando o servidor está pouco ocupado. O terceiro melhora quando o servidor está ocupado. Estes valores competem entre si, por isso nenhum número isolado descreve um servidor de serving.
Corrija os comprimentos de entrada e saída antes de medir
O throughput depende do formato do tráfego. Um prompt com 4,000 tokens e uma resposta com 50 tokens é um trabalho dominado pelo prefill. Um prompt com 200 tokens e uma resposta com 2,000 tokens é um trabalho dominado pelo decode. O mesmo servidor apresenta valores muito diferentes de tokens por segundo nesses dois casos. Por isso, escolha uma proporção, registe-a ao lado de cada valor e nunca compare proporções diferentes. 1,024 tokens de entrada e 1,024 tokens de saída é um padrão razoável, porque vários fornecedores publicam resultados com essa proporção. Se conhece o seu tráfego real, use o seu tráfego real.
Force também o comprimento da saída. Um modelo que encontra o token de paragem após 60 tokens produz uma execução mais curta, que não pode ser comparada com uma execução completa. O TTFT representa uma parcela maior dessa execução, o que reduz o throughput de saída medido pelo benchmark, embora cada pedido termine mais cedo. A flag --ignore-eos no cliente de benchmark do vLLM faz com que cada pedido gere exatamente o número de tokens solicitado. Assim, as duas execuções permanecem comparáveis. A escolha do modelo altera estes valores mais do que qualquer flag: instalar um modelo Qwen 3 numa GPU de uma VPS aborda o aspeto da memória dessa escolha.
Meça primeiro um único fluxo
Comece pelo caso mais simples. É uma verificação de sanidade e também estabelece um limite superior. O Ollama apresenta as próprias medições de tempo.
ollama run llama3.1:8b --verbose "Write 200 words about disk scheduling."A linha a consultar é eval rate, que corresponde ao número de tokens de saída por segundo. prompt eval rate é a taxa de prefill, e load duration é o tempo gasto a carregar o modelo para a VRAM. Na primeira chamada depois de um arranque a frio, load duration é elevado, por isso total duration é enganador. Execute o comando duas vezes e leia o segundo resultado. Por predefinição, o Ollama descarrega um modelo inativo ao fim de cinco minutos. Uma pausa longa entre execuções faz regressar ao caso de arranque a frio.
Os mesmos campos são disponibilizados pela API, o que facilita a criação de scripts.
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))}'eval_duration está em nanossegundos. Dividir por 1,000,000,000 dá o valor em segundos. Essa é exatamente a divisão indicada pela documentação da API do Ollama para calcular tokens por segundo. Se o servidor ainda não estiver em execução, alojar um LLM com Ollama num VPS explica a instalação e a unidade systemd.
O TTFT requer um pedido com streaming, e o curl pode medir esse tempo.
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}'time_starttransfer é o instante em que chega o primeiro byte do corpo da resposta. Numa conclusão de chat com streaming, esse byte pertence ao primeiro evento enviado pelo servidor. Pode ser o primeiro token de conteúdo ou um delta que contém apenas a função, enviado imediatamente antes dele. Por isso, considere o valor como TTFT com uma margem de um evento. É suficientemente preciso para comparar duas execuções no mesmo servidor.
Os números de um único fluxo favorecem o servidor de duas formas. O TTFT é o melhor valor possível, porque não existe nenhum pedido à sua frente na fila. A taxa por fluxo também é a melhor possível, porque a placa inteira está a processar um único pedido. Nenhum dos dois valores indica a carga que o servidor consegue suportar.
Como executar uma varredura de concorrência?
Uma varredura executa a mesma carga de trabalho fixa com níveis de concorrência crescentes e regista o resultado de cada etapa. O vLLM fornece o cliente para isso, e ele usa a OpenAI API, por isso também funciona com o Ollama e com qualquer outro serviço compatível com a OpenAI API.
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,99--max-concurrency limita os pedidos em curso e é a variável que está a ser avaliada. --num-prompts é o total enviado, por isso mantenha-o próximo de dez vezes a concorrência para obter uma média estável. O resumo apresenta Output token throughput (tok/s): e Total token throughput (tok/s):, seguidos de Mean TTFT (ms):, Median TTFT (ms): e P99 TTFT (ms): sob um cabeçalho Time to First Token.
A saída não apresenta a taxa por fluxo, mas é possível calculá-la com uma divisão. Mean TPOT (ms): é o tempo médio por token de saída depois do primeiro, por isso 25 ms por token correspondem a 40 tokens por segundo por fluxo. Dividir o débito de saída pela concorrência produz o mesmo resultado.
Depois, repita o processo e guarde cada execução.
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"
doneLeitura dos ficheiros JSON guardados
Cada execução grava um ficheiro. Assim, extraia de uma só vez os campos necessários de todos eles.
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"
doneoutput_throughput é o número de tokens de saída por segundo. total_token_throughput inclui também os tokens de entrada, por isso, numa proporção de 1:1, fica próximo do dobro. p99_ttft_ms só existe porque --metric-percentiles incluiu 99; se pedir um percentil que não solicitou, o jq apresenta null.
O que mostra realmente um sweep de concorrência?
Os dados por trás deste gráfico
[
{
"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
}
]Estas 6 linhas ilustram o formato que um sweep produz numa máquina GPU pequena, disponível para aluguer, com ordens de grandeza plausíveis. Não são uma medição do seu servidor nem um valor fornecido pelo fabricante. Execute o loop acima e substitua-as pelos seus valores.
Analise o formato, porque é o formato que se generaliza. Com um stream, a máquina inteira produz 92 tokens por segundo, com um TTFT p99 de 61 ms. Em 128 streams, o total chega a 2304 tokens por segundo, vinte e cinco vezes mais, enquanto cada stream individual desce para 18 tokens por segundo e o TTFT p99 chega a 3820 ms. O débito total aumenta porque o batching transforma esperas de memória ociosas em trabalho útil. A velocidade por stream diminui porque o mesmo processamento passa a ser partilhado.
A última duplicação é o indicador decisivo. Passar de 64 para 128 streams acrescenta menos de seis por cento ao total, enquanto o TTFT p99 quase triplica. Isto significa que os pedidos estão a aguardar numa fila em vez de serem processados, seja porque a cache KV está cheia, seja porque o servidor atingiu um limite de agendamento, como o max_num_seqs do vLLM. O ponto de operação útil surge antes: com 32 streams, a máquina ainda devolve 1728 tokens por segundo, 75 por cento do pico, a 54 tokens por segundo por stream e com um TTFT p99 de 498 ms. Registe esse ponto como a sua capacidade. Não pode atender utilizadores no pico da curva.
Ollama e vLLM não medem a mesma coisa
Execute essa varredura contra um servidor Ollama com a configuração padrão e o total quase não mudará. OLLAMA_NUM_PARALLEL tem o valor padrão 1, portanto uma solicitação é executada enquanto as restantes aguardam. É a fila que faz o TTFT p99 aumentar, enquanto o total de saída permanece estável. Aumente esse valor antes de medir qualquer coisa.
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_NUM_PARALLEL=8"Reinicie com sudo systemctl restart ollama e confirme que o modelo continua a caber. Cada slot paralelo recebe a sua própria parcela da janela de contexto. Por isso, a documentação do Ollama indica que um contexto de 2K com 4 solicitações paralelas aloca 8K. Se aumentar demasiado o número de slots, o modelo excede a VRAM. Verifique ollama ps: uma coluna PROCESSOR com um valor semelhante a 48%/52% CPU/GPU significa que parte do modelo está na CPU. Nesse caso, o débito começará a diminuir à medida que aumenta a concorrência, em vez de aumentar. Depois de esgotar os slots paralelos, as solicitações aguardam até OLLAMA_MAX_QUEUE, que tem o valor padrão 512. Depois disso, o servidor responde com 503.
O vLLM usa batching contínuo. Assim, admite novas solicitações no batch em execução à medida que os slots ficam disponíveis. A curva continua a subir até que algum recurso se esgote, normalmente a cache KV. Um limite configurado, como max_num_seqs, ou a sobrecarga do agendamento da CPU também pode fazê-la estabilizar mais cedo. O Ollama é otimizado para um modelo, uma máquina e baixo custo de configuração. Por isso, os dois engines produzem respostas diferentes para a mesma varredura. Esse é o verdadeiro tema de Ollama e vLLM comparados como engines de serving. Registe qual engine e qual versão produziram cada valor.
Cinco formas de medir a coisa errada
- O cliente está longe. Fazer o benchmark a partir do portátil através da Internet acrescenta a sua ida e volta a cada TTFT, pelo que está a medir a sua ligação doméstica. Execute o cliente na mesma região que o servidor.
- O modelo estava frio. O primeiro pedido inclui o carregamento dos pesos e, no vLLM, também pode incluir a captura do grafo. Envie um lote de aquecimento e descarte o resultado.
- O cache de prefixos respondeu por si. O vLLM ativa o cache automático de prefixos por predefinição. Por isso, enviar repetidamente o mesmo prompt mede o cache em vez do prefill, e o TTFT fica reduzido a uma fração do valor real.
--dataset-name randomevita isto porque cada prompt é diferente. Para garantir esse comportamento, inicie o servidor com--no-enable-prefix-caching. - As respostas eram curtas. Com respostas de 32 tokens, o TTFT domina cada pedido e os seus tokens por segundo descrevem, na realidade, o prefill. Use
--ignore-eoscom um comprimento de saída realista. - Só comunicou a concorrência 1. Esse valor produz o TTFT mais favorável na tabela e só é a entrada correta para o custo se os seus pedidos reais nunca se sobrepuserem. Se houver sobreposição, o custo depende do throughput que consegue manter com a concorrência gerada pelo seu tráfego real. Meça também nesse nível.
Transforme o número medido numa decisão
Pegue no throughput de saída que o seu teste mostra na concorrência atingida pelo tráfego real e compare-o com o preço por token. Esse é o valor de saturação apenas se houver pedidos suficientes em simultâneo para manter o batch cheio. Uma carga de trabalho que envia um pedido de cada vez tem de usar a sua taxa de fluxo único. O ponto de equilíbrio resulta de uma divisão, depois de contabilizar os tokens de entrada que chegam com cada token de saída:
cost_per_million_output = price_out + price_in * (input_tokens / output_tokens)
break_even_tok_s = (price_per_hour / cost_per_million_output) * 1000000 / 3600Aplique o cálculo aos preços da DigitalOcean de julho de 2026. O endpoint dedicado de inferência H200 custava $4.47 por hora. O Llama 3.3 70B serverless custava $0.65 por milhão de tokens de entrada e $0.65 por milhão de tokens de saída. Com a proporção 1:1 usada acima, cada milhão de tokens de saída inclui um milhão de tokens de entrada. O preço efetivo é, portanto, $1.30 por milhão de tokens de saída. Assim, 4.47 dividido por 1.30 corresponde a cerca de 3.44 milhões de tokens de saída por hora. Dividindo por 3,600 segundos, obtemos cerca de 955 tokens de saída por segundo, ou cerca de 1,910 tokens por segundo contando a entrada e a saída em conjunto. Os preços são da DigitalOcean. A divisão é nossa. Quando verificámos em 10 de outubro de 2026, o endpoint H200 continuava a custar $4.47 por hora. No entanto, a DigitalOcean descontinuou o Llama 3.3 70B serverless em 18 de agosto de 2026 e encaminha os utilizadores para o Llama 4 Maverick, listado a $0.25 por milhão de tokens de entrada e $0.87 por milhão de tokens de saída. É um modelo diferente, por isso precisa do seu próprio teste antes de a comparação ter significado.
A palavra decisiva é sustentado. Atingir 955 tokens de saída por segundo em saturação durante duas horas por dia não significa manter 955 tokens de saída por segundo, porque também paga as outras vinte e duas horas. Alugar a placa como GPU Droplet não reduz o limiar. Em 10 de outubro de 2026, os preços dos Droplets da DigitalOcean indicavam o H200 a $4.47 por hora sob demanda, o mesmo preço do endpoint. Os seus NVIDIA Droplets mais baratos, o RTX 4000 a $0.76 por hora e o L40S ou RTX 6000 a $1.57 por hora, são demasiado pequenos para alojar um modelo 70B em FP8. Portanto, com os valores acima, um H200 que atinge 2,036 tokens de saída por segundo em saturação tem de manter uma média de cerca de 47 por cento desse pico em todas as horas pagas apenas para igualar $0.65 por milhão de tokens de entrada e de saída. Abaixo desse valor, o preço por token é mais vantajoso. Normalmente, são as horas de GPU sem utilização, e não os tokens lentos, que tornam o self-hosting inviável.
A sua decisão tem, portanto, dois dados de entrada. O teste fornece o limite máximo. O padrão do seu tráfego fornece a fração desse limite que realmente consegue utilizar. Multiplique os dois valores e consulte o ponto de equilíbrio entre o GPU VPS e a API com preço por token para obter a resposta relativa ao seu volume.
FAQ
Qual é uma boa velocidade de tokens por segundo para um LLM autoalojado?
Há duas respostas, porque esta métrica serve para duas finalidades. Para uma pessoa que lê a saída, qualquer valor acima de aproximadamente 20 tokens de saída por segundo por stream já é mais rápido do que a leitura. A partir daí, uma velocidade maior não traz benefício. Para o custo, o valor relevante é o débito total de saída que consegue manter com a sua carga real. Uma velocidade é boa quando permite atingir o ponto de equilíbrio. Com um custo de $0.65 por milhão de tokens, tanto de entrada como de saída, num servidor que custa $4.47 por hora, esse limite fica perto de 955 tokens de saída por segundo, mantidos numa proporção de 1:1 e com os preços de July 2026. Um único stream num modelo grande nunca o atinge. É por isso que existe o processamento em lotes.
Porque é que o débito do meu Ollama fica constante quando adiciono pedidos concorrentes?
OLLAMA_NUM_PARALLEL tem o valor predefinido 1. Por isso, o servidor processa um pedido de cada vez por modelo e coloca os restantes numa fila, até OLLAMA_MAX_QUEUE (512 por predefinição), antes de devolver 503. A saída total mantém-se constante enquanto o TTFT p99 aumenta. Este é o padrão de uma fila, não de uma GPU ocupada. Defina a variável num drop-in do systemd e reinicie o serviço. Depois, verifique ollama ps, porque cada slot paralelo multiplica o contexto alocado e pode fazer com que parte do modelo seja executada na CPU.
Devo medir o tempo até ao primeiro token ou os tokens por segundo?
Ambos, porque evoluem em sentidos opostos à medida que a carga aumenta. O TTFT é o que o utilizador sente. O débito de saída com o sistema saturado é o que se reflete na fatura. Registe o TTFT p50 e p99 em cada nível de concorrência. Depois, escolha o nível de concorrência mais elevado em que o TTFT p99 ainda é aceitável para si. Apresente o débito nesse ponto como a sua capacidade. Não use o valor máximo no topo da curva.
Um valor mais elevado de tokens por segundo significa sempre um custo menor por token?
Não. O custo por token é o preço por hora dividido pelo número de tokens que o servidor produziu efetivamente nessa hora. Por isso, um servidor rápido que permanece inativo durante a maior parte do dia continua a ter um custo elevado por token. O fator decisivo é a utilização, não a velocidade máxima. Tenha também atenção às unidades. Um débito total de tokens indicado inclui os tokens de entrada. Numa proporção de entrada para saída de 1:1, esse valor é próximo do dobro da velocidade de saída. Se a API que está a comparar também cobrar os tokens de entrada, inclua as duas partes no cálculo antes de comparar.