Modelagem de performance é a arte de prever comportamento de sistemas sem precisar testá-los exaustivamente. Com modelos corretos, você pode responder perguntas como "quantos usuários aguenta?" sem executar testes caros.
Modelo é mapa, não território. Útil para navegar, perigoso para confiar cegamente.
Por Que Modelar
Custos de testes
Teste de carga real:
- Infraestrutura: $500-5000
- Tempo de preparação: 2-5 dias
- Execução: 1-4 horas
- Análise: 1-2 dias
Modelo matemático:
- Cálculo: minutos
- Custo: $0
- Iterações: ilimitadas
Quando modelar vale a pena
✓ Capacity planning antes de comprar infra
✓ Estimativas rápidas para stakeholders
✓ Comparar cenários hipotéticos
✓ Entender limites teóricos
✓ Validar intuição antes de testar
Fundamentos: Little's Law
A lei mais útil em modelagem de performance:
L = λ × W
L = número médio de itens no sistema
λ = taxa de chegada (throughput)
W = tempo médio no sistema (latência)
Exemplos práticos
1. Conexões de banco de dados
Throughput: 100 queries/s
Latência média: 50ms = 0.05s
Conexões ativas = 100 × 0.05 = 5 conexões
2. Capacidade de servidores
Cada servidor aguenta 100 conexões simultâneas
Latência por request: 200ms
Throughput por servidor = 100 / 0.2 = 500 req/s
3. Dimensionando infraestrutura
Meta: 10.000 req/s
Latência esperada: 100ms
Conexões simultâneas = 10.000 × 0.1 = 1.000
Se cada servidor aguenta 200 conexões:
Servidores necessários = 1.000 / 200 = 5
Teoria de Filas
Modelo M/M/1
Sistema com uma fila e um servidor:
λ = taxa de chegada
μ = taxa de serviço
ρ = λ/μ (utilização)
Para sistema estável: ρ < 1
Tempo médio no sistema:
W = 1 / (μ - λ)
Exemplo:
Chegadas: 80 req/s (λ)
Serviço: 100 req/s (μ)
Utilização: 80/100 = 80%
Tempo no sistema: 1/(100-80) = 50ms
Utilização vs Latência:
ρ = 50%: W = 1/(100-50) = 20ms
ρ = 80%: W = 1/(100-80) = 50ms
ρ = 90%: W = 1/(100-90) = 100ms
ρ = 95%: W = 1/(100-95) = 200ms
ρ = 99%: W = 1/(100-99) = 1000ms
→ Latência explode perto de 100% utilização
Modelo M/M/c
Múltiplos servidores:
c = número de servidores
Para sistema estável: ρ = λ/(c×μ) < 1
Exemplo: Pool de conexões
Requisições: 200 req/s
Tempo por query: 20ms (μ = 50/s)
Com 1 conexão: 200/50 = 4 (instável!)
Com 4 conexões: 200/(4×50) = 1 (limite!)
Com 5 conexões: 200/(5×50) = 0.8 (estável)
Com 10 conexões: 200/(10×50) = 0.4 (margem)
Universal Scalability Law (USL)
Modelo que captura limites de escalabilidade:
C(N) = N / (1 + σ(N-1) + κN(N-1))
N = número de processadores/servidores
σ = coeficiente de contenção
κ = coeficiente de coerência
C(N) = capacidade relativa
Interpretação
σ (sigma): overhead de serialização
- Seções críticas, locks
- Quanto maior σ, pior escala
κ (kappa): overhead de coordenação
- Comunicação entre nós
- Quanto maior κ, pior escala
Perfis de escalabilidade
σ = 0, κ = 0: Linear (ideal)
N servidores = N× capacidade
σ > 0, κ = 0: Sublinear
Escala, mas com retornos decrescentes
σ > 0, κ > 0: Retrograde
Existe ponto máximo, depois piora
Exemplo prático:
Sistema com σ=0.1, κ=0.01
N=1: C = 1.0
N=2: C = 1.82 (1.82x)
N=4: C = 3.08 (0.77x eficiência)
N=8: C = 4.68 (0.58x eficiência)
N=16: C = 5.76 (0.36x eficiência)
N=32: C = 5.41 (pior que 16!)
→ Máximo ~22 servidores para esse sistema
Aplicando Modelos
Passo 1: Medir parâmetros
# Medir taxa de serviço (μ)
latencies = collect_latencies(sample_size=1000)
mu = 1 / mean(latencies)
# Medir taxa de chegada (λ)
arrivals = count_arrivals(window='1 minute')
lambda_ = arrivals / 60
# Calcular utilização
rho = lambda_ / mu
Passo 2: Validar modelo
# Calcular previsão
predicted_latency = 1 / (mu - lambda_)
# Comparar com observado
observed_latency = mean(latencies)
error = abs(predicted - observed) / observed
if error > 0.2:
print("Modelo não se aplica bem")
Passo 3: Projetar
# Quanto aguenta com latência < 100ms?
target_latency = 0.1 # 100ms
# W = 1/(μ-λ) → λ = μ - 1/W
max_lambda = mu - (1 / target_latency)
print(f"Capacidade máxima: {max_lambda} req/s")
Limitações de Modelos
1. Distribuições assumidas
Modelos M/M/c assumem:
- Chegadas Poisson (exponenciais)
- Serviço exponencial
Realidade:
- Chegadas em rajadas
- Serviço com variância alta
- Dependências entre requests
2. Sistema fechado vs aberto
Sistema aberto: chegadas independentes
Sistema fechado: N usuários fixos
Modelo errado = previsão errada
3. Warm-up e estados
Modelo assume steady-state
Ignora:
- Cold start
- Cache warming
- JIT compilation
- Connection pooling
4. Dependências
Modelo de um componente ignora:
- Latência de rede
- Dependências externas
- Contenção em recursos compartilhados
Modelo Prático Simplificado
Quando modelos formais são complexos demais:
Regra dos 80%
Não opere acima de 80% de utilização
Margem para picos e variância
Fator de segurança
Capacidade necessária = Pico × 1.5
Se pico esperado = 1000 req/s
Provisione para 1500 req/s
Extrapolação linear com margem
Atual: 2 servidores, 500 req/s
Meta: 2000 req/s
Linear: 2000/500 × 2 = 8 servidores
Com margem 50%: 12 servidores
Com overhead de coordenação: 15 servidores
Ferramenta: Calculadora Rápida
def capacity_estimate(
current_throughput: float,
current_servers: int,
target_throughput: float,
efficiency: float = 0.7 # Assume 70% efficiency
) -> int:
"""
Estima servidores necessários
"""
throughput_per_server = current_throughput / current_servers
ideal_servers = target_throughput / throughput_per_server
real_servers = ideal_servers / efficiency
return ceil(real_servers)
# Exemplo
servers = capacity_estimate(
current_throughput=1000,
current_servers=4,
target_throughput=5000
)
print(f"Servidores necessários: {servers}")
# Output: Servidores necessários: 29
Conclusão
Modelos de performance são ferramentas poderosas, mas com limitações:
Use modelos para:
- Estimativas rápidas antes de testes
- Capacity planning inicial
- Identificar limites teóricos
- Comparar cenários hipotéticos
Não use modelos para:
- Decisões finais sem validação
- Sistemas complexos com muitas dependências
- Garantias de performance
Workflow recomendado:
1. Modele → Estimativa inicial
2. Teste → Validação do modelo
3. Ajuste → Refine parâmetros
4. Monitore → Validação contínua
Todos os modelos estão errados, mas alguns são úteis. — George Box