Streaming, batching e latência

TL;DR

A experiência de velocidade de um LLM é definida por duas métricas: TTFT (tempo até o primeiro token aparecer) e TPOT (tempo entre tokens seguintes). TTFT depende do tamanho do input (fase prefill); TPOT depende do hardware de inferência (fase decode). Streaming via SSE é obrigatório para UX responsiva. Batching aumenta throughput mas pode degradar TTFT individual. Em 2026, a fronteira é “inferência desagregada” — prefill e decode em hardware separado.

O que o usuário experimenta — e por que importa

O usuário abre a caixa de chat, digita a pergunta e pressiona Enter. Agora ele está olhando para uma tela em branco. Quanto tempo vai ficar assim?

Se forem 3 segundos, a maioria dos usuários assume que algo travou. Se for 300ms, parece instantâneo. Essa percepção não depende da qualidade da resposta — depende de quanto tempo a caixa fica vazia. E o tempo que a caixa fica vazia é o TTFT: Time To First Token, o tempo até o primeiro caractere aparecer.

Mas tem um segundo fenômeno: depois que os tokens começam a aparecer, com que velocidade eles chegam? Um modelo que gera 5 tokens por segundo parece lento mesmo que o TTFT seja baixo — o usuário vê a resposta “gotejando”. Um modelo que gera 60 tokens por segundo parece rápido mesmo que tenha demorado 800ms para começar.

TTFT e velocidade de geração são dois gargalos separados, com causas físicas separadas, e otimizados por meios diferentes. Confundir os dois é construir o produto errado.

Latência em LLMs não é um número único

É um sistema de trade-offs entre três dimensões:

graph TD
    A["📊 O trilemma da inferência"] --> B["⏱️ Latência\n(velocidade por request)"]
    A --> C["📈 Throughput\n(requests por segundo)"]
    A --> D["💰 Custo\n(compute por request)"]
    B -. "otimizar um\ndegrada outro" .-> C
    C -. "otimizar um\ndegrada outro" .-> D
    D -. "otimizar um\ndegrada outro" .-> B
    style B fill:#99ccff,stroke:#0066cc
    style C fill:#99ff99,stroke:#009900
    style D fill:#ffcc99,stroke:#cc6600

Otimizar uma frequentemente degrada outra. O trabalho do engenheiro é encontrar o equilíbrio certo para cada caso de uso.

As duas fases da inferência

graph LR
    A["Input: N tokens"] --> B["Fase Prefill\n(compute-bound)\nTodos os tokens\nprocessados em paralelo"]
    B --> C["Primeiro token\n← TTFT medido aqui"]
    C --> D["Fase Decode\n(memory-bound)\nUm token por vez\nKV cache inteiro relido"]
    D --> E["Token 2"]
    D --> F["Token 3"]
    D --> G["..."]
    D --> H["Último token"]
    style B fill:#99ccff,stroke:#0066cc
    style D fill:#ffcc99,stroke:#cc6600
FaseO que fazBottleneckMétrica
PrefillProcessa todos os input tokens, cria KV cacheCompute (FLOPs)TTFT
DecodeGera tokens autoregressivamente, um por vezMemória (bandwidth)TPOT / ITL

Ver os detalhes físicos em 04a - KV cache, prefill e decode — a física da inferência.

Métricas de performance

MétricaO que medeBomAceitávelRuim
TTFTTempo até o primeiro token<500ms500ms–2s>2s
TPOT / ITLTempo entre tokens consecutivos<30ms30–80ms>100ms
TPSTokens por segundo (output)>50 tps20–50 tps<20 tps
E2E latencyTempo total da chamadaDepende do output
xychart-beta
    title "Percepção de velocidade pelo usuário (TTFT)"
    x-axis ["<200ms", "200-500ms", "500ms-2s", "2s-5s", ">5s"]
    y-axis "% usuários satisfeitos" 0 --> 100
    bar [98, 90, 65, 30, 8]

P99 importa mais que a média

Se o TTFT médio é 300ms mas P99 é 5s, 1 em cada 100 requests parece que “travou”. Usuários que experimentam P99 ruim formam opiniões negativas sobre o produto inteiro. Monitore P50, P95, P99 — não só média.

Streaming via SSE

Server-Sent Events (SSE) é o protocolo padrão para streaming de LLMs. Em vez de esperar a resposta completa, o servidor envia tokens incrementalmente:

# Request com streaming
POST /v1/chat/completions
{"model": "claude-sonnet-4.6", "stream": true, "messages": [...]}

# Response (SSE)
data: {"type":"content_block_delta","delta":{"text":"Aqui"}}
data: {"type":"content_block_delta","delta":{"text":" está"}}
data: {"type":"content_block_delta","delta":{"text":" o"}}
data: {"type":"content_block_delta","delta":{"text":" código"}}
data: {"type":"message_stop"}

Por que streaming é obrigatório:

  • Percepção de velocidade — o usuário vê progresso imediatamente, mesmo que o tempo total seja igual
  • Early termination — se a resposta já está errada, o usuário pode cancelar sem esperar o output completo
  • Progress feedback — em agentes, mostra o “pensamento” do modelo em tempo real

Batching e seus trade-offs

Tipo de batchingComo funcionaImpacto
Static batchingAgrupa N requests, processa juntas, retorna juntasThroughput alto, latência individual alta
Continuous batchingInsere/remove requests do batch a cada iteraçãoThroughput alto, latência individual moderada
Dynamic batchingAjusta batch size baseado em load e SLOsMelhor equilíbrio

Continuous batching é o estado da arte em 2026 (usado por vLLM, TGI, TensorRT-LLM):

  • Quando um request no batch termina, seu slot é imediatamente preenchido por um novo request
  • Isso mantém a GPU ocupada sem fazer novos requests esperarem pelo batch inteiro

Speculative decoding — gerar mais rápido sem mudar o modelo

Speculative decoding é uma das otimizações mais engenhosas de 2024-2026. A ideia: o decode é lento porque gera um token por vez com o modelo principal (grande, caro). E se um modelo menor (draft model) gerasse 5 tokens de uma vez como “especulação”, e o modelo principal verificasse todos os 5 em paralelo?

graph TD
    subgraph "Decode tradicional — 5 tokens em 5 steps"
        M1["Modelo 70B\nStep 1 → 'O'"]
        M2["Modelo 70B\nStep 2 → ' código'"]
        M3["Modelo 70B\nStep 3 → ' faz'"]
        M4["Modelo 70B\nStep 4 → ' isso'"]
        M5["Modelo 70B\nStep 5 → ':'"]
        M1 --> M2 --> M3 --> M4 --> M5
    end
    subgraph "Speculative decoding — 5 tokens em ~1.5 steps"
        D1["Draft model (7B)\nGera: 'O código faz isso:'"]
        V1["Modelo 70B verifica\ntodos em paralelo\n→ aceita 4, rejeita ':'\n→ gera o token correto"]
        D1 --> V1
    end
    style M1 fill:#ff9999,stroke:#cc0000
    style D1 fill:#99ccff,stroke:#0066cc
    style V1 fill:#99ff99,stroke:#009900

Se o draft model especula bem (taxa de aceitação alta), o throughput pode dobrar ou triplicar sem mudar a distribuição de probabilidade do modelo principal — a verificação garante que o resultado seja matematicamente idêntico ao que o modelo principal geraria.

Otimizações de latência (2026)

OtimizaçãoO que fazGanho
Prefix cachingReutiliza KV cache de prefixos comunsTTFT -50-85%
FlashAttention 3Computação de atenção I/O-aware2-4x mais rápido
Speculative decodingDraft model propõe, principal verifica em batchThroughput 2-3x
Quantização (INT8/INT4)Reduz tamanho dos pesosTPOT -30-50%, mais batches
Inferência desagregadaPrefill e decode em GPUs separadasTTFT e throughput otimizados independentemente

Inferência desagregada: a fronteira

Em 2026, a técnica mais avançada é separar prefill e decode em hardware diferente:

graph LR
    subgraph "Cluster Prefill (Compute-heavy)"
        A["GPU H100 SXM\n(alto FLOP/s)\nProcessar input tokens\n→ TTFT otimizado"]
        B["Gerar KV Cache"]
        A --> B
    end
    subgraph "Cluster Decode (Memory-heavy)"
        C["GPU H100 NVL\n(alto HBM bandwidth)\nGerar tokens autoregressivamente\n→ throughput otimizado"]
    end
    B -->|"Transferir KV Cache\n(pode ser GBs)"| C
    style A fill:#99ccff,stroke:#0066cc
    style C fill:#ffcc99,stroke:#cc6600

Benefício: cada cluster é otimizado para seu bottleneck específico. Custo: a transferência do KV cache entre GPUs adiciona latência e usa largura de banda de rede.

Quando usar / quando não usar

CenárioStreaming?Batching?Modelo rápido?
Chat interativo✅ Sempre❌ Latência individual✅ Flash/Nano
Agente de coding✅ Sempre❌ Sequencial⚠️ Depende da tarefa
Geração de testes em massa❌ Opcional✅ Batch API✅ Budget
Pipeline de dados❌ Não✅ Batch API + concurrent✅ Budget

Armadilhas

"Streaming é mais rápido"

Não. O tempo total é o mesmo. Streaming melhora a percepção de velocidade, não a velocidade real.

Otimizar só TTFT

Em agentes, TPOT importa mais porque a resposta precisa estar completa antes de prosseguir para o próximo step.

Ignorar P99 latency

Média de TTFT pode ser 300ms, mas P99 pode ser 5s. O tail latency é o que o usuário percebe como “travou”.

"GPU mais cara = mais rápida"

Nem sempre. Para decode, bandwidth de memória importa mais que compute. Uma A100 pode perder para hardware com HBM3.

Não configurar timeouts

Sem timeout, uma chamada que trava pode bloquear um pipeline inteiro. Configure 30-60s para interativo, 5-10min para batch.

Como explicar em inglês

LLM performance involves two independent metrics: TTFT (time-to-first-token) measures how long before the first character appears — driven by the prefill phase (processing the input prompt), which is compute-bound. TPOT (time per output token) measures how fast tokens stream after the first — driven by the decode phase, which is memory-bandwidth-bound. Streaming via SSE makes the blank-box wait feel shorter (the user sees progress immediately) without changing the total time. Batching improves GPU utilization (and therefore throughput) by processing multiple requests together, but can increase individual TTFT. The frontier optimization in 2026 is prefill-decode disaggregation — splitting the two phases onto separate hardware, each optimized for its bottleneck.

PTEN
Tempo até o primeiro tokenTime To First Token (TTFT)
Tempo entre tokensTime Per Output Token (TPOT)
Tokens por segundoTokens per second (TPS)
Latência de ponta a pontaEnd-to-end latency
Latência de caudaTail latency (P99)
Eventos enviados pelo servidorServer-Sent Events (SSE)
Cancelamento antecipadoEarly termination
Agrupamento de requisiçõesBatching / request batching
Agrupamento contínuoContinuous batching
Decodificação especulativaSpeculative decoding
Inferência desagregadaPrefill-decode disaggregation

Ver mais

O que vem a seguir

Tudo até aqui assumiu que o modelo gera tokens de forma direta: prefill, decode, um token de cada vez (ou especulado em lote, no caso do speculative decoding), sem pausa para “pensar” antes de responder. Mas há uma classe de modelos que quebra essa premissa — os reasoning models, que geram uma cadeia de raciocínio (chain-of-thought) antes da resposta final. Isso muda drasticamente o perfil de latência: o TTFT deixa de ser “tempo até o primeiro token útil” e passa a incluir um bloco inteiro de tokens de raciocínio que o usuário nem sempre vê. Ver 15 - Reasoning models e chain-of-thought para como isso reconfigura os trade-offs de streaming, batching e percepção de velocidade discutidos nesta nota.

Veja também

Referências

  • Kwon et al.Efficient Memory Management for Large Language Model Serving with PagedAttention (vLLM, 2023). O paper que revolucionou batching e serving de LLMs.
  • BentoMLThe LLM Inference Trilemma (2025). Framework para pensar em latência vs throughput vs custo.
  • NVIDIATensorRT-LLM Best Practices (2026). Guia de otimização de inferência para H100.
  • DatabricksContinuous Batching Explained (2025). Explicação acessível do conceito com benchmarks.