SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-24

Por que seu LLM trava com 5 usuários simultâneos

Entenda por que 1 usuário funciona e 5 ficam lentos: batching, cache KV, prefill e fila definem quantas pessoas seu servidor LLM atende ao mesmo tempo.

Por que um LLM auto-hospedado fica mais lento quando chegam mais utilizadores?

Um LLM auto-hospedado fica bloqueado com 5 utilizadores concorrentes porque o servidor ainda está a gerar uma resposta de cada vez, enquanto os outros quatro aguardam numa fila. A documentação do Ollama é clara quanto ao valor predefinido: OLLAMA_NUM_PARALLEL é "o número máximo de pedidos paralelos que cada modelo processa ao mesmo tempo, predefinição 1." Não há nenhuma avaria. Quatro das cinco pessoas estão à espera da sua vez.

A solução raramente é usar um servidor maior. É utilizar um motor de serving que processe muitos pedidos numa única passagem forward pelo modelo, além de ter memória livre suficiente para manter as conversas de todos os utilizadores durante esse processamento. Ambas as partes são importantes, mas a segunda é o que define efetivamente o seu limite.

As duas fases pelas quais cada pedido passa

O prefill lê todo o prompt de uma só vez e constrói a cache de atenção correspondente. Todos os tokens do prompt passam pelo modelo em conjunto. Por isso, o prefill é uma grande multiplicação de matrizes e fica limitado pela capacidade de processamento aritmético. O decode escreve a resposta um token de cada vez. Para cada token, é necessário ler novamente todos os pesos do modelo a partir da memória, enquanto o processamento aritmético desse único token é reduzido. O decode fica limitado pela largura de banda da memória.

Essa assimetria é precisamente o motivo pelo qual o batching funciona. O decode de um utilizador lê, por exemplo, 5 GB de pesos por token e deixa a maior parte das unidades aritméticas sem utilização. Ao adicionar um segundo pedido, o motor lê os mesmos 5 GB uma vez e calcula depois dois tokens a partir deles. O segundo utilizador quase não acrescenta tempo. Processar os pedidos estritamente um após outro elimina essa vantagem.

Dois números descrevem a experiência do utilizador. TTFT (tempo até ao primeiro token) é o tempo de espera na fila mais o prefill. ITL (latência entre tokens) é o intervalo entre os tokens transmitidos e é determinado pelo decode. Um servidor lento costuma ser lento num destes aspetos, e as correções não são as mesmas. Vale a pena determinar qual deles está a causar o problema antes de alterar qualquer definição. medir o prefill e o decode separadamente é a forma de o descobrir.

O batching estático faz todos esperarem pela resposta mais lenta

O batching estático é a versão básica. É o que obtém quando agrupa pedidos manualmente no código da aplicação. O motor recolhe N pedidos, executa-os em conjunto e mantém todas as posições ocupadas até terminar a geração mais longa do grupo.

Um utilizador que pede um resumo de 1.200 tokens mantém quatro respostas de uma linha bloqueadas no batch, porque o batch não liberta nenhuma posição até terminar o seu membro mais lento.

Daqui resultam dois custos. As sequências concluídas continuam a ocupar posições sem executar trabalho útil, pelo que o throughput efetivo diminui quando os comprimentos das respostas variam. Os comprimentos das respostas variam bastante nos chats. Um pedido que chega um passo depois da formação do batch espera que todo o batch termine antes de sequer iniciar o prefill. Isto faz com que o seu TTFT seja determinado pelo texto longo de outro utilizador.

O batching contínuo admite e remove pedidos a cada token

O batching contínuo agenda pedidos ao nível de um único passo de descodificação. Depois de cada passo, o agendador remove as sequências que acabaram de emitir o respetivo token de paragem e admite os pedidos em espera nos slots livres. Uma resposta que termina no passo 40 liberta o respetivo slot no passo 40, e não no fim de um batch.

Isto não é algo exótico. llama-server documenta -cb, --cont-batching como "se deve ativar o batching contínuo (também conhecido como batching dinâmico) (predefinição: ativado)", e o vLLM foi concebido em torno dessa ideia. O Ollama também processa pedidos em paralelo. A predefinição limita o número a um, e é por isso que tantas pessoas concluem que o seu hardware não suporta concorrência, quando foi a configuração que a desativou.

Os resultados publicados sobre batching contínuo são normalmente medidos em placas de datacenter com capacidade de computação disponível e dezenas de gigabytes para a cache. O comportamento desses resultados aplica-se ao seu sistema. A escala dos resultados não se aplica, e a secção sobre memória abaixo explica porquê.

O prefill compete com o decode pelo mesmo poder de processamento

Quando chega um novo pedido enquanto quatro respostas estão a ser transmitidas, o seu prompt tem de passar primeiro pelo prefill, que exige muito poder de processamento. Se o agendador atribuir a esse prefill um passo próprio, os quatro utilizadores que estão a receber respostas não recebem nenhum token durante esse período. Num prompt longo, isto provoca uma pausa visível em todas as janelas abertas. É a interrupção a que as pessoas se referem quando dizem que o servidor engasga sempre que outra pessoa clica em enviar.

O prefill em blocos divide um prompt longo em partes e mistura cada parte no mesmo passo que os decodes em execução. O guia de ajuste do vLLM declara diretamente o compromisso: orçamentos de blocos menores "achieve better ITL because there are fewer prefills slowing down decodes", enquanto valores maiores "achieve better time to first token (TTFT) as you can process more prefill tokens in a batch". Está a escolher qual experiência proteger: a pessoa que espera pelo início da resposta ou as pessoas que veem o texto a ser transmitido.

O comprimento do prompt determina o impacto. Um prompt com 6,000 tokens e uma resposta com 200 tokens representa 6,000 tokens de trabalho de prefill contra 200 passos de decode. O chat com geração aumentada por recuperação e os prompts de sistema longos colocam ambos o sistema nesse regime. Assim, o prefill deixa de ser um custo residual e passa a ser aquilo por que os utilizadores esperam. O cache de prefixo ajuda quando a parte longa se repete: o vLLM expõe --enable-prefix-caching, que reutiliza o cache de um prefixo de prompt partilhado em vez de o recalcular para cada pedido.

A memória que esgota primeiro é a cache KV

Cada token de cada conversa ativa deixa um vetor de chave e um vetor de valor em cada camada do modelo. Essa é a cache KV (cache de chave/valor). É ela que permite ao processo de decodificação evitar recalcular todo o prompt para cada token novo. O tamanho por token é fixado pela arquitetura do modelo: 2 (uma chave e um valor) vezes o número de camadas, vezes o número de cabeças de chave/valor, vezes a dimensão da cabeça, vezes o número de bytes por valor. Consulte esses números no config.json do modelo.

Faça este cálculo uma vez e o limite deixa de ser um mistério. Um modelo 8B típico, com 36 camadas, 8 cabeças de chave/valor e dimensão de cabeça de 128, mantendo a cache em 16 bits, consome 2 36 8 128 2 bytes por token. Isso corresponde a 147,456 bytes, cerca de 144 KiB. Uma conversa com 8,192 tokens precisa, portanto, de aproximadamente 1.2 GB de cache. Cinco conversas precisam de cerca de 6 GB, além dos pesos. Essa é a resposta real para saber quantos utilizadores cabem.

A simultaneidade multiplica o contexto, e as ferramentas mostram isso claramente. A FAQ do Ollama diz: "Parallel request processing for a given model results in increasing the context size by the number of parallel requests. For example, a 2K context with 4 parallel requests will result in an 8K context and additional memory allocation." A RAM necessária cresce com OLLAMA_NUM_PARALLEL multiplicado por OLLAMA_CONTEXT_LENGTH. Em llama-server, o contexto solicitado com -c é distribuído pelos -np slots. Por isso, aumentar apenas o número de slots reduz o que cada pedido pode manter. Consulte o contexto por slot no log de arranque, em vez de o assumir.

O vLLM faz uma pré-alocação. --gpu-memory-utilization (por predefinição, 0.92) é "the fraction of GPU memory to be used for the model executor". A memória que sobra depois dos pesos torna-se o pool KV paginado. Quando esse pool fica sem espaço, o scheduler remove um pedido em vez de o deixar falhar:

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.

No motor V1 do vLLM, o modo de preempção predefinido é RECOMPUTE. Por isso, um pedido removido perde a sua cache e volta a fazer o prefill quando é readmitido. Esse trabalho é executado duas vezes. A documentação avisa que "preemption and recomputation can adversely affect end-to-end latency". Esta linha do log é a melhor explicação para um utilizador ter esperado muito mais tempo do que os restantes, enquanto a média parecia normal. Defina disable_log_stats=False para registar a contagem acumulada ou consulte o contador de preempções nas métricas Prometheus expostas pelo vLLM.

O que muda com 2, 5 e 20 utilizadores concorrentes

Dois utilizadores. Num GPU com cache disponível, o impacto é quase invisível, porque o segundo fluxo de decodificação acompanha o primeiro com muito pouco tempo adicional. Num VPS apenas com CPU e 4 a 8 GB de RAM, não é gratuito: os dois fluxos partilham os mesmos poucos vCPUs e a mesma largura de banda da RAM. Assim, cada utilizador obtém aproximadamente metade dos tokens por segundo, e a procura de cache duplica face a um orçamento muito menor.

Cinco utilizadores. É aqui que as predefinições deixam de ser suficientes, e o problema começa por ser uma questão de fila. Com OLLAMA_NUM_PARALLEL em 1, quatro pessoas esperam por quem pediu a resposta longa, e cada uma vê a velocidade normal quando chega a sua vez. Se aumentar a contagem de processos paralelos, o problema muda de natureza: cinco slots com um contexto de 8K cada exigem uma cache de 40K tokens. Se não couber na VRAM, o motor transfere camadas para a RAM do sistema. Se também não couber na RAM, o sistema começa a usar swap e os tokens por segundo entram em colapso.

Vinte utilizadores. Vinte pessoas numa interface de chat normalmente não correspondem a vinte pedidos concorrentes, e esta é a ideia mais importante a compreender antes de comprar hardware. Uma pessoa lê uma resposta e demora 20 a 60 segundos entre interações, por isso a maior parte da sessão está inativa. Vinte agentes ou vinte trabalhos de sumarização de documentos correspondem a vinte fluxos reais sem qualquer tempo de inatividade. Isso exige uma máquina diferente. Um programador que tenha apontado um agente de programação para o seu próprio servidor Ollama está mais próximo do segundo caso do que do primeiro, porque o agente continua a enviar pedidos enquanto a tarefa decorre e não inclui as pausas de leitura que uma pessoa faz.

Os seus utilizadores são concorrentes ou apenas estão autenticados?

Calcule os pedidos em curso antes de dimensionar qualquer componente. A aritmética é simples: pedidos em curso é igual ao número de utilizadores, multiplicado pelos segundos gastos a gerar cada turno, dividido pelo número de segundos entre turnos.

  1. Meça primeiro a velocidade do seu próprio fluxo, tanto no prefill como no decode. Não reutilize um valor obtido com a placa de outra pessoa: meça os tokens por segundo no seu próprio sistema e use o resultado obtido.
  2. Estime o ciclo de utilização. Vinte utilizadores de chat, 12 segundos de geração por turno e um turno a cada 90 segundos resultam em 20 * 12 / 90, ou seja, cerca de 2.7 pedidos em curso.
  3. Defina o número de slots ligeiramente acima desse valor e confirme-o em relação à memória: o número de slots multiplicado pelo contexto por pedido tem de caber nos tokens de cache que realmente estão disponíveis.
  4. Mantenha a fila curta para que o excesso falhe rapidamente e de forma visível.

Os tokens de cache disponíveis correspondem à memória livre depois de carregar os pesos, dividida pelo custo por token indicado na secção anterior. Uma placa de 24 GB que execute um modelo 8B em 16-bit usa cerca de 16 GB para os pesos e tem aproximadamente 6 GB de cache utilizável com a utilização predefinida, o que corresponde a cerca de cinco conversas de 8K. Para suportar mais conversas, reduza o contexto por pedido ou armazene a cache em 8-bit (llama-server ocupa --cache-type-k q8_0). Ambas as opções aumentam a concorrência através de uma cedência. Vale a pena ler a descrição honesta dessa troca antes de investir em hardware: quando um GPU VPS atinge o ponto de equilíbrio face aos tokens de API.

Quando os valores predefinidos do Ollama deixam de ser suficientes

Aumente a contagem de execuções paralelas através da unidade do serviço, porque uma exportação feita na shell não chega a um daemon gerido pelo systemd.

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 deve apresentar as três variáveis que acabou de definir. Se isso não acontecer, o drop-in não foi guardado, e nada do que fizer depois terá efeito. ollama ps lista então o modelo carregado com um tamanho superior ao dos pesos isolados, porque quatro slots com 8,192 tokens reservam 32,768 tokens de cache adicional. Uma coluna PROCESSOR que mostre parte do modelo na CPU quando esperava que estivesse todo na GPU significa que pediu mais cache do que a placa tinha disponível. Reduza um dos dois valores. Reduzir o contexto é normalmente a opção mais segura, mas uma janela demasiado pequena trunca silenciosamente os prompts longos em vez de gerar um erro. Por isso, vale a pena dimensionar num_ctx de forma deliberada em vez de o reduzir até o modelo caber.

O valor predefinido da fila merece uma segunda análise. O Ollama coloca em fila até OLLAMA_MAX_QUEUE pedidos, e "o valor predefinido é 512". Depois desse limite, responde "com um erro 503 que indica que o servidor está sobrecarregado". Uma fila com profundidade 512 num servidor que atende quatro pedidos de cada vez é uma promessa que não pode cumprir, porque o cliente na posição 300 excede o tempo limite muito antes de chegar a sua vez. Uma fila curta devolve um erro que a aplicação pode repetir ou comunicar, o que é preferível a um indicador de espera que nunca termina.

Teste na prática. Envie dois pedidos exatamente ao mesmo tempo a partir de dois terminais e observe ambos. Se o segundo não produzir nada até o primeiro terminar, a configuração de execução paralela não foi aplicada.

Quando um motor de serving dedicado começa a compensar

O vLLM compensa a configuração adicional quando tem uma GPU com margem disponível e mais de aproximadamente quatro pedidos efetivamente em processamento. O agendador funciona por token, a cache é paginada para reutilizar fragmentos livres e a VRAM disponível é convertida em concorrência, em vez de ficar ociosa. Em agosto de 2026, a instalação e o arranque documentados são feitos com dois comandos:

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."}]
    }'

Uma resposta que contenha uma matriz choices significa que o servidor está ativo e o modelo foi carregado. Sob carga, as duas opções relevantes são --max-num-seqs, o "número máximo de sequências processadas numa única iteração", e --max-num-batched-tokens, o "número máximo de tokens que podem ser processados numa única iteração". A primeira limita a concorrência. A segunda define o orçamento de prefill em blocos descrito anteriormente.

Com menos de aproximadamente quatro pedidos em processamento, ou em qualquer máquina sem uma GPU compatível, o vLLM acrescenta complexidade e oferece poucos benefícios. Espera uma placa da classe CUDA e reserva a maior parte da memória no arranque, o que é uma troca inadequada numa VPS com 4 a 8 GB. Nesse caso, a opção correta é um modelo menor, com um contexto mais curto e uma fila que possa controlar. como o Ollama e o vLLM diferem como motores de serving explica a escolha em detalhe, e executar o Qwen 3 8B numa VPS mostra o que um modelo de tamanho intermédio exige antes de adicionar um único utilizador extra.

O compromisso que o folclore esconde

O batching contínuo aumenta o throughput total e normalmente também melhora a latência mediana, porque um pedido em fila começa mais cedo. A latência de cauda evolui no sentido oposto, e essa parte raramente é mencionada.

Cada sequência adicional num passo acrescenta algum trabalho, por isso o ITL aumenta para todos à medida que o batch fica cheio. O prefill de uma nova chegada ocupa uma parte de um passo que, de outro modo, estaria disponível para os utilizadores em streaming. Sob pressão de cache, o scheduler faz preempção, o que envia um pedido parcialmente gerado de volta ao início do seu prefill.

Uma interface de chat mostra as caudas, não as médias. Um stream que pausa durante dois segundos a meio de uma frase parece estar avariado, mesmo quando o tempo total até à conclusão é bom. Meça o p95 TTFT e o p95 ITL sob a carga esperada e trate a média de tokens por segundo como um valor de capacidade, não como uma descrição da experiência.

A configuração prática resulta daí. Limite a concorrência ligeiramente abaixo do que a memória permite, para que o motor nunca precise de fazer preempção. Uma fila curta e previsível é melhor do que um batch profundo que entra em thrashing, porque um utilizador que espera quatro segundos e depois recebe um stream contínuo fica mais satisfeito do que um utilizador que começa imediatamente e sofre duas paragens.

O que verificar quando está lento

Cada utilizador tem um desempenho normal, mas a espera é longa. Isso é uma fila, não um problema de velocidade. Verifique primeiro a definição de paralelismo. O modelo está a responder corretamente, uma solicitação de cada vez.

HTTP 503 do Ollama. A fila está cheia. O servidor está no limite da capacidade ou OLLAMA_MAX_QUEUE foi definido com um valor baixo de propósito para rejeitar carga, que é precisamente o comportamento pretendido.

Os tokens por segundo diminuem drasticamente sob carga num servidor com CPU. Execute vmstat 1 enquanto isso acontece. Valores diferentes de zero nas colunas si e so significam que a máquina está a usar swap. Nesse caso, os pesos são lidos do disco a cada token. Nenhuma alteração de configuração resolve isso. Reduza o tamanho do modelo ou o número de slots.

Um em cada dez utilizadores espera muito mais do que os restantes. Procure preempted no log do vLLM. A preempção e o respetivo recálculo são normalmente a causa. Isso significa que a cache é insuficiente para o comprimento de contexto permitido.

O TTFT é elevado mesmo quando o servidor está inativo. Isso é prefill, não concorrência. Prompts longos consomem tempo real antes de surgir o primeiro token. Por isso, verifique o tamanho do prompt e o caching de prefixos antes de analisar o hardware. Se a espera longa ocorrer apenas com a primeira pessoa que regressa depois de um período sem atividade e todos os pedidos seguintes forem normais, então não é prefill. Nesse caso, o Ollama descarregou o modelo e voltou a ler os pesos do disco. Vale a pena excluir essa hipótese mantendo o modelo residente entre pedidos.

FAQ

Por que o meu LLM auto-hospedado fica mais lento quando uma segunda pessoa o utiliza?

Na maioria dos casos, não fica mais lento. Entra numa fila. O Ollama é distribuído com OLLAMA_NUM_PARALLEL definido como 1, por isso o segundo pedido espera que o primeiro produza o token final. Distinga os dois casos medindo o fluxo de tokens de um utilizador enquanto outro espera: se a velocidade em tokens por segundo for normal assim que começar, existe uma fila, e aumentar o número de pedidos em paralelo resolve o problema. Se os dois fluxos funcionarem a metade da velocidade, está a partilhar efetivamente a largura de banda da memória, que é uma limitação do hardware.

Quantos utilizadores simultâneos pode servir uma GPU pequena?

Calcule a memória, não o número de utilizadores. Primeiro, os pesos. Depois, a cache KV, que consome 2 vezes o número de camadas vezes o número de cabeças de chave/valor vezes a dimensão da cabeça vezes o número de bytes, por token e por conversa ativa. Um modelo típico de 8B com 36 camadas, 8 cabeças de chave/valor e dimensão de cabeça 128 consome cerca de 144 KiB por token em 16-bit. Assim, uma conversa com 8,192 tokens precisa de aproximadamente 1.2 GB. Uma placa de 24 GB que contenha esse modelo em 16-bit fica com cerca de 6 GB para a cache. Isso corresponde a cerca de cinco conversas com o contexto completo, ou a mais conversas se reduzir o contexto.

O batching contínuo torna a resposta de cada utilizador mais lenta?

A latência mediana normalmente melhora, porque os pedidos deixam de esperar que um batch inteiro termine. A latência de cauda piora. Cada sequência adicional acrescenta trabalho a cada passo de decoding, a fase de prefill de um novo pedido ocupa parte de um passo dos utilizadores que estão a receber o fluxo, e um pedido preempted tem de executar o prefill duas vezes. Meça a latência p95 entre tokens, não a média, porque uma janela de chat torna as pausas evidentes de uma forma que as médias ocultam.

Devo aumentar OLLAMA_NUM_PARALLEL ou mudar para vLLM?

Aumente primeiro o número de pedidos em paralelo. Não tem custo e requer apenas um ficheiro drop-in. Isto resolve o caso comum em que quatro pessoas ficam numa fila atrás de uma resposta longa. A memória é o limite: os pedidos em paralelo multiplicam o contexto que tem de manter, por isso monitorize a passagem de camadas para a CPU. Mude para vLLM quando tiver uma GPU com VRAM disponível e mais de cerca de quatro pedidos realmente em execução, porque é nesse ponto que a cache paginada e o agendamento por token compensam mais do que custam.

Mais núcleos de CPU corrigem um servidor LLM lento?

Não na parte que os utilizadores mais notam. O decoding lê o modelo inteiro da memória para cada token, por isso é limitado pela largura de banda da RAM. Os núcleos adicionais deixam de ajudar quando a largura de banda fica saturada. O prefill escala com o número de núcleos, por isso mais núcleos reduzem o tempo até ao primeiro token em prompts longos. Numa VPS com 4 a 8 GB, a limitação principal costuma ser a capacidade da memória. A solução eficaz é normalmente um modelo mais pequeno ou um contexto mais curto, não mais vCPUs.