A mentalidade de desempenho de John Carmack para gráficos em tempo real
Guia prático da mentalidade orientada ao desempenho associada a John Carmack: profiling, orçamentos de tempo por frame, trade-offs e como entregar sistemas complexos em tempo real.

Por que a abordagem do Carmack ainda importa
John Carmack costuma ser tratado como uma lenda dos motores de jogo, mas a parte útil não é a mitologia — são os hábitos repetíveis. Não se trata de copiar o estilo de uma pessoa ou presumir “movimentos de gênio”. Trata-se de princípios práticos que levam, de forma confiável, a software mais rápido e suave, especialmente quando prazos e complexidade se acumulam.
Engenharia de desempenho, em termos simples
Engenharia de desempenho significa fazer o software atingir uma meta de velocidade em hardware real, sob condições reais — sem quebrar a corretude. Não é “deixar rápido a qualquer custo”. É um loop disciplinado:
- decidir o que significa “rápido o suficiente”
- medir onde o tempo está realmente sendo gasto
- mudar uma coisa de propósito
- verificar que você melhorou a métrica certa
Essa mentalidade aparece no trabalho do Carmack repetidas vezes: discuta com dados, mantenha mudanças explicáveis e prefira abordagens que você consiga manter.
Por que gráficos em tempo real expõem a realidade
Gráficos em tempo real são implacáveis porque há um prazo a cada frame. Se você o perde, o usuário sente imediatamente como stutter, latência de entrada ou movimento irregular. Outros softwares podem esconder ineficiências atrás de filas, telas de carregamento ou trabalho em background. Um renderer não negocia: ou você termina a tempo, ou não.
É por isso que as lições se generalizam além dos jogos. Qualquer sistema com requisitos rígidos de latência — UI, áudio, AR/VR, trading, robótica — se beneficia de pensar em orçamentos, entender gargalos e evitar picos-surpresa.
O que você vai levar daqui
Você receberá checklists, heurísticas e padrões de decisão aplicáveis ao seu trabalho: como definir orçamentos de tempo por frame (ou latência), como perfilar antes de otimizar, como escolher a “uma coisa” para corrigir e como prevenir regressões para que desempenho vire rotina — não um pânico de última hora.
Pense em orçamentos de tempo por frame, não em sensações
O pensamento de desempenho ao estilo Carmack começa com uma troca simples: pare de falar de “FPS” como unidade primária e comece a falar sobre tempo por frame.
FPS é um recíproco (“60 FPS” soa bem, “55 FPS” soa próximo), mas a experiência do usuário é guiada por quanto tempo cada frame demora — e, igualmente importante, quão consistentes são esses tempos. Um salto de 16.6 ms para 33.3 ms é visível instantaneamente, mesmo que sua média de FPS ainda pareça respeitável.
Tempo por frame vs. FPS (por que tempo por frame vence)
- FPS esconde variabilidade. Duas builds podem “ter média de 60 FPS”, mas uma pode stutterar devido a frames ocasionais de 40–60 ms.
- Tempo por frame mapeia para trabalho. Cada milissegundo é uma fatia real de trabalho da CPU/GPU que você pode atribuir a sistemas.
- Metas ficam mais claras. “Ficar abaixo de 16.6 ms” é um requisito concreto; “parecer suave” não é.
Orçamentos: o que você realmente está gastando
Um produto em tempo real tem múltiplos orçamentos, não apenas “renderizar mais rápido”:
- Tempo de CPU (lógica do jogo, animação, culling, submissão de draw calls)
- Tempo de GPU (shading, pós-processamento, overdraw, resolução)
- Memória (pegada, picos, fragmentação, margem para streaming)
- Tempo de carregamento (boot, carregamento de níveis, compilação de shaders, stalls de streaming)
Esses orçamentos interagem. Economizar tempo de GPU adicionando batchs pesados na CPU pode sair pela culatra, e reduzir memória pode aumentar custos de streaming ou descompressão.
Exemplo: 16.6 ms a 60 FPS
Se sua meta é 60 FPS, seu orçamento total é 16.6 ms por frame. Uma divisão aproximada pode ser:
- CPU: 7 ms (simulação, gameplay, visibilidade)
- GPU: 9 ms (render + pós)
- OS/driver + buffer de overhead: ~0.6 ms
Se CPU ou GPU ultrapassarem o orçamento, você perde o frame. Por isso equipes falam em ser “CPU-bound” ou “GPU-bound” — não como rótulos, mas como forma de decidir de onde realisticamente virá o próximo milissegundo.
“Rápido o suficiente” é um requisito de produto
O ponto não é perseguir uma métrica de vaidade como “maior FPS num PC topo de linha”. O ponto é definir o que é rápido o suficiente para seu público — alvos de hardware, resolução, limites de bateria, térmicas e responsividade de entrada — e então tratar o desempenho como orçamentos explícitos que você pode gerenciar e defender.
Perfilamento primeiro: meça, depois decida
O movimento padrão do Carmack não é “otimizar”, é “verificar”. Problemas de desempenho em tempo real estão cheios de histórias plausíveis — pausas de GC, “shaders lentos”, “muitos draw calls” — e a maioria delas está errada na sua build no seu hardware. Perfilar é como substituir intuição por evidência.
Comece pela medição (antes de chutar)
Trate o profiling como uma feature de primeira classe, não como ferramenta de resgate de última hora. Capture tempos por frame, timelines de CPU e GPU, e as contagens que os explicam (triângulos, draw calls, mudanças de estado, alocações, faltas de cache se puder). O objetivo é responder uma pergunta: onde o tempo está realmente indo?
Um modelo útil: em todo frame lento, uma coisa é o fator limitante. Talvez seja a GPU presa em um passe pesado, a CPU presa na atualização de animação, ou a thread principal travada em sincronização. Encontre essa restrição primeiro; o resto é ruído.
Itere como um cientista
Um loop disciplinado evita que você fique girando em falso:
- Meça uma baseline com uma cena e caminho de câmera repetíveis
- Mude uma coisa
- Re-meça e escreva o delta
Se a melhoria não for clara, presuma que não ajudou — porque provavelmente não vai sobreviver ao próximo conteúdo.
Cuidado com otimizações placebos
Trabalho de desempenho é especialmente vulnerável à autoenganação:
- Erros de benchmarking: cenas de teste inconsistentes, builds de debug, tarefas em background, throttling térmico, diferenças de vsync
- Viés de confirmação: “parece mais rápido” sem dados de tempo por frame
- Médias enganosas: uma média melhor pode esconder picos piores
Perfilar primeiro mantém seu esforço focado, suas trocas justificadas e suas mudanças mais fáceis de defender em revisão.
Gargalos: encontre a única coisa que está realmente lenta
Problemas de desempenho em tempo real parecem bagunçados porque tudo acontece ao mesmo tempo: gameplay, render, streaming, animação, UI, física. O instinto do Carmack é cortar o ruído e identificar o limitador dominante — a única coisa que está definindo seu tempo por frame no momento.
Categorias comuns de gargalos
A maioria das lentidões cai em alguns grupos:
- CPU-bound: a thread principal (ou um worker crítico) não consegue terminar seu trabalho a tempo — lógica, submissão de draw calls, física, avaliação de animação.
- GPU-bound: a GPU não consegue terminar o frame — shaders pesados, muitos pixels, pós-processamento caro, geometria complexa.
- Memory-bound: limitado por largura de banda/latência — faltas de cache, layout de dados ruim, muitos acessos aleatórios, cópias grandes de buffers.
- I/O-bound: streaming de assets, compilação de shaders, descompressão, leituras de arquivo, esperas de rede.
O objetivo não é rotular para um relatório — é puxar a alavanca certa.
Maneiras rápidas de diagnosticar (antes de reescrever algo)
Alguns experimentos rápidos podem dizer o que está realmente no controle:
- Teste de escala de resolução: reduza resolução de render (ou force dynamic resolution). Se o tempo por frame melhorar muito, provavelmente você é limitado por GPU/pixel. Se mal mudar, olhe para CPU ou trabalho de GPU que não depende de pixels.
- Toggle de features: desligue sombras, SSR, AO, partículas ou passes caros um por um. Uma mudança significativa revela onde o tempo está indo.
- Instrumentação e captures: use timers embutidos, um profiler de CPU e uma captura de GPU para ver onde os milissegundos realmente caem.
Princípio da “uma grande pedra”
Raramente você vence aparando 1% em dez sistemas. Encontre o maior custo que se repete a cada frame e ataque isso primeiro. Remover um único ofensor de 4 ms vale mais que semanas de micro-otimizações.
Os gargalos se movem
Depois de consertar a pedra grande, a próxima pedra grande fica visível. Isso é normal. Trate o trabalho de desempenho como um loop: medir → mudar → re-medir → re-priorizar. O objetivo não é um perfil perfeito; é progresso constante rumo a tempo por frame previsível.
Suavidade vence: picos, stutter e latência da cauda
A média do tempo por frame pode parecer boa enquanto a experiência ainda é ruim. Gráficos em tempo real são julgados pelos piores momentos: o frame perdido durante uma grande explosão, o travamento ao entrar em uma nova sala, o stutter súbito quando um menu abre. Isso é latência da cauda — frames lentos, raros, mas frequentes o suficiente para os usuários notarem.
Por que as caudas importam mais que médias
Um jogo rodando a 16.6 ms a maior parte do tempo (60 FPS) mas pulando para 60–120 ms a cada poucos segundos parecerá “quebrado”, mesmo que a média ainda seja 20 ms. Humanos são sensíveis ao ritmo. Um único frame longo quebra a previsibilidade da entrada, o movimento da câmera e o sync áudio/visual.
Fontes comuns de picos
Picos frequentemente vêm de trabalho que não é espalhado uniformemente:
- Garbage collection ou page faults de memória que pausam o mundo
- Compilação de shader e criação de pipeline disparadas “just in time”
- Streaming de assets que de repente precisa de descompressão, uploads ou I/O de arquivo
- Escalonamento do OS e trabalho em background roubando CPU (ou mudanças de frequência/termais)
Estratégias para reduzir stutter
O objetivo é tornar o trabalho caro previsível:
- Pré-compute o que for possível: compile shaders offline, bake dados, prepare tabelas de consulta.
- Aqueça cedo: compile shaders, crie pipelines, toque assets críticos durante telas de carregamento ou uma cena de warm-up controlada.
- Amortize tarefas caras: espalhe streaming, descompressão e uploads por muitos frames ao invés de um só.
- Limite trabalho por frame: imponha orçamentos de tempo (por exemplo, “no máximo 2 ms para streaming este frame”) e difira o restante.
Registre e visualize a cauda
Não apenas trace uma linha de FPS média. Registre tempos por frame e visualize:
- Histogramas de tempo por frame para ver agrupamentos e outliers
- Percentis (p95, p99, p99.9) para rastrear explicitamente a cauda
- Marcadores de picos com eventos correlacionados (início de GC, compilação de shader, load de asset)
Se você não consegue explicar seus piores 1% de frames, você não explicou realmente o desempenho.
Torne as trocas explícitas (Qualidade vs Velocidade vs Complexidade)
O trabalho de desempenho fica mais fácil no momento em que você para de fingir que pode ter tudo ao mesmo tempo. O estilo do Carmack empurra equipes a nomear a troca em voz alta: o que estamos comprando, o que estamos pagando e quem sente a diferença?
Nomeie os eixos (e o custo real)
A maioria das decisões se situa em alguns eixos:
- Qualidade: fidelidade visual, precisão da simulação, sensação de entrada
- Velocidade: tempo por frame, tempo de carregamento, tempo de compilação, tempo de iteração
- Memória: VRAM, RAM, largura de banda
- Complexidade: depuração mais difícil, mais casos de borda, maior carga de testes
- Tempo para entregar: risco de cronograma, risco de integração, foco da equipe
Se uma mudança melhora um eixo mas taxa silenciosamente três outros, documente isso. “Isso adiciona 0.4 ms de GPU e 80 MB de VRAM para ganhar sombras mais suaves” é uma declaração utilizável. “Fica melhor” não é.
Defina limites de “bom o suficiente”
Gráficos em tempo real não tratam de perfeição; tratam de atingir uma meta com consistência. Concorde com limites como:
- FPS mínimo / tempo máximo por frame numa máquina de referência
- picos aceitáveis no pior caso (não apenas média)
- tetos de memória por plataforma
Uma vez que a equipe concorda que, digamos, 16.6 ms a 1080p na GPU base é a meta, as discussões ficam concretas: essa feature nos mantém dentro do orçamento ou força um downgrade em outro lugar?
Prefira decisões reversíveis
Quando estiver incerto, escolha opções que você possa desfazer:
- feature flags para efeitos arriscados
- configurações escaláveis (baixo/médio/alto) que mapeiem para custos reais
- caminhos de fallback para hardware antigo
A reversibilidade protege o cronograma. Você pode lançar o caminho seguro e deixar o ambicioso atrás de um toggle.
Otimize o que os usuários sentem
Evite overengineering por ganhos invisíveis. Uma melhoria de 1% na média raramente vale um mês de complexidade — a menos que remova stutter, corrija latência de entrada ou previna um crash por memória. Priorize mudanças que os jogadores notam imediatamente e deixe o resto para depois.
Disciplina de engenharia: corretude habilita velocidade
O trabalho de desempenho fica dramaticamente mais fácil quando o programa está correto. Uma quantidade surpreendente de tempo gasto em “otimização” é na verdade perseguir bugs de corretude que apenas parecem problemas de desempenho: um loop O(N²) acidental por trabalho duplicado, um passe de render rodando duas vezes porque uma flag não foi resetada, um vazamento de memória que aumenta gradualmente o tempo por frame, ou uma condição de corrida que vira stutter aleatório.
Trate a corretude como uma ferramenta de desempenho
Um engine estável e previsível dá medidas limpas. Se o comportamento muda entre execuções, você não pode confiar nos profiles e acabará otimizando ruído.
Práticas de engenharia disciplinadas ajudam a acelerar:
- Invariantes claras: defina o que deve ser sempre verdade (por exemplo, “cada objeto visível é submetido uma vez”, “recursos GPU não são mutados enquanto estão em voo”, “frame graph não tem ciclos”).
- Validação em builds de debug: adicione asserts e cheques leves que gritem cedo — antes que um estado quebrado vire hitching misterioso. Valide tamanhos de buffer, transições de estado e que alocações por frame fiquem dentro de um limite conhecido.
Faça bugs de desempenho reprodutíveis sob demanda
Muitos picos de tempo por frame são “Heisenbugs”: desaparecem quando você adiciona logging ou depura passo a passo. O antídoto é reprodução determinística.
Construa um pequeno harness de teste controlado:
- Cenas de teste mínimas que isolam uma feature (sombras, partículas, UI, streaming)
- Caminhos de câmera fixos e entrada roteirizada para que cada execução seja comparável
- Configurações travadas (resolução, nível de qualidade, time step fixo quando possível) para remover variáveis
Quando um hitch aparece, você quer um botão que o reproduza 100 vezes — não um relato vago de que “às vezes acontece depois de 10 minutos”.
Mude menos, aprenda mais
Trabalho de performance se beneficia de mudanças pequenas e revisáveis. Grandes refactors criam múltiplos modos de falha de uma vez: regressões, novas alocações e trabalho extra oculto. Diffs menores facilitam responder à única pergunta que importa: o que mudou no tempo por frame, e por quê?
Disciplina aqui não é burocracia — é como manter as medições confiáveis para que otimização vire algo direto e não superstição.
Trabalhe com a máquina: dados, cache e overhead
Desempenho em tempo real não é só sobre “código mais rápido”. É sobre organizar o trabalho para que CPU e GPU possam executá-lo eficientemente. Carmack enfatizou repetidamente uma verdade simples: a máquina é literal. Ela ama dados previsíveis e odeia overhead evitável.
Pensamento orientado a dados: facilite leitura de memória
CPUs modernas são incrivelmente rápidas — até esperarem por memória. Se seus dados estão espalhados por muitos objetos pequenos, a CPU passa tempo perseguindo ponteiros em vez de fazer matemática.
Um modelo mental útil: não faça dez viagens ao mercado para dez itens. Coloque-os em um carrinho e percorra os corredores uma vez. Em código, isso significa manter valores usados frequentemente próximos (frequentemente em arrays ou structs compactos) para que cada fetch de cache line traga dados que você realmente vai usar.
Padrões de alocação: churn pequeno vira dor grande
Alocações frequentes criam custos escondidos: overhead do alocador, fragmentação de memória e pausas imprevisíveis quando o sistema precisa arrumar as coisas. Mesmo que cada alocação seja “pequena”, um fluxo constante pode se tornar um imposto pago por frame.
Correções comuns são propositalmente sem graça: reutilize buffers, faça pools de objetos e prefira alocações de longa vida para caminhos quentes. O objetivo não é esperteza — é consistência.
Batching: reduza overhead antes de otimizar a matemática
Uma quantidade surpreendente de tempo por frame pode desaparecer em bookkeeping: mudanças de estado, draw calls, trabalho do driver, syscalls e coordenação de threads.
Batching é a versão “um grande carrinho” do rendering e da simulação. Em vez de emitir muitas operações minúsculas, agrupe trabalho similar para cruzar fronteiras caras menos vezes. Muitas vezes, cortar overhead vence micro-otimizar um shader ou loop interior — porque a máquina passa menos tempo se preparando para trabalhar e mais tempo realmente trabalhando.
Simplicidade como estratégia de desempenho
Trabalho de desempenho não é só código mais rápido — é também ter menos código. Complexidade tem um custo que você paga todo dia: bugs demoram mais para isolar, correções exigem testes mais cuidadosos, iteração desacelera porque cada mudança toca mais peças, e regressões surgem por caminhos raramente usados. Essa complexidade não só consome tempo de desenvolvedor; frequentemente adiciona overhead em tempo de execução (branches extras, alocações, faltas de cache, sincronização) que é difícil de ver até que seja tarde.
O imposto oculto da complexidade
Um sistema “inteligente” pode parecer elegante até você estar num prazo e um pico de frame aparecer apenas em um mapa, uma GPU ou uma combinação de configurações. Cada flag extra, caminho de fallback e caso especial multiplica o número de comportamentos que você precisa entender e medir. Essa complexidade não só desperdiça tempo de dev; muitas vezes adiciona overhead de runtime difícil de detectar.
Prefira soluções que você consegue explicar
Uma boa regra: se você não consegue explicar o modelo de desempenho para um colega em poucas frases, provavelmente não consegue otimizá-lo de forma confiável.
Soluções simples têm duas vantagens:
- São mais fáceis de perfilar e raciocinar (menos variáveis)
- Reduzem “unknown unknowns”, onde um ajuste menor causa lentidões inesperadas
“Deletar código” é uma ferramenta real de otimização
Às vezes, o caminho mais rápido é remover uma feature, cortar uma opção ou colapsar variantes múltiplas em uma só. Menos features = menos caminhos de código, menos combinações de estado e menos lugares onde o desempenho pode degradar silenciosamente.
Deletar código também é um movimento de qualidade: o melhor bug é o que você elimina removendo o módulo que poderia gerá-lo.
Refatorar ou corrigir? Lista rápida para decidir
Patch (correção cirúrgica) quando:
- você identificou um hot path específico e uma pequena mudança melhora mensuravelmente
- o sistema é estável e amplamente usado; mudar arquitetura arrisca novas regressões
- você precisa de uma melhoria segura que cabe no ciclo de release atual
Refactor (simplificar estrutura) quando:
- o profiling aponta overhead espalhado por muitos call sites ou camadas
- você costuma re-quebrar desempenho na mesma área após mudanças não relacionadas
- o código exige conhecimento tribal para ser modificado com segurança
- você pode deletar ou mesclar caminhos e ficar com menos conceitos no total
Simplicidade não é “menos ambição”. É escolher designs que continuam compreensíveis sob pressão — quando desempenho importa mais.
Evite regressões: transforme performance em hábito
O trabalho de desempenho só permanece se você consegue dizer quando ele escorrega. É isso que é teste de regressão de desempenho: uma maneira repetível de detectar quando uma mudança torna o produto mais lento, menos suave ou mais pesado em memória.
Diferente dos testes funcionais (que respondem “funciona?”), testes de regressão respondem “continua com a mesma sensação de velocidade?”. Uma build pode estar 100% correta e ainda ser uma má release se acrescentar 4 ms de tempo por frame ou dobrar o tempo de carregamento.
Um fluxo leve que realmente é usado
Você não precisa de um laboratório para começar — apenas consistência.
Escolha um pequeno conjunto de cenas base que representem uso real: uma visão pesada na GPU, uma visão pesada na CPU e uma cena de stress “pior caso”. Mantenha-as estáveis e roteirize a câmera e inputs para que cada execução seja idêntica.
Rode os testes em hardware fixo (um PC/console/devkit conhecido). Se você mudar drivers, OS ou configurações de clock, registre isso. Trate a combinação hardware/software como parte do fixture de teste.
Armazene resultados em um histórico versionado: hash do commit, configuração do build, ID da máquina e métricas medidas. O objetivo não é um número perfeito — é uma linha de tendência confiável.
Métricas amigáveis a CI para rastrear
Prefira métricas difíceis de discutir:
- Percentis de tempo por frame (p50/p95/p99), não apenas FPS médio. Percentis mostram stutter e hitches de cauda.
- Memória de pico (e picos de alocação). Creep de memória muitas vezes aparece antes de crashes.
- Tempo de carregamento (cold start e transições de nível/cena), porque jogadores notam segundos mais que micro-otimizações.
Defina limiares simples (por exemplo: p95 não deve regredir mais que 5%).
O que fazer quando encontrar uma regressão
Trate regressões como bugs: atribua um dono e um prazo.
Primeiro, bisecte para achar a mudança que a introduziu. Se a regressão bloquear uma release, reverta rápido e re-lande com a correção.
Ao consertar, adicione guardrails: mantenha o teste, deixe uma nota no código e documente o orçamento esperado. O hábito é a vitória — desempenho vira algo que você mantém, não algo que você “faz depois”.
Entregue sistemas complexos: desempenho, prazos e realidade
“Shippar” não é um evento no calendário — é um requisito de engenharia. Um sistema que só roda bem no laboratório, ou que só atinge tempo por frame depois de uma semana de ajustes manuais, não está pronto. A mentalidade do Carmack trata restrições do mundo real (variedade de hardware, conteúdo bagunçado, comportamento imprevisível dos jogadores) como parte da especificação desde o dia um.
Entregar significa escolher o que deve ser verdade
Quando você está perto do release, perfeição vale menos que previsibilidade. Defina os não negociáveis em termos claros: FPS alvo, picos de tempo por frame, limites de memória e tempos de carregamento. Então trate qualquer coisa que viole isso como bug, não “polish”. Isso reframeia trabalho de desempenho de otimização opcional para trabalho de confiabilidade.
Priorize o que os jogadores realmente sentem
Nem todas as lentidões importam igualmente. Conserte primeiro os problemas visíveis ao usuário:
- Stutter e picos costumam melhorar mais a percepção do que renderização ligeiramente mais lenta.
- Travamentos de menu, pops de streaming e latência de entrada frequentemente prejudicam mais do que uma pequena queda no FPS médio.
- Regressões em cenários comuns (combate intenso, giros de câmera, momentos com muitos efeitos) merecem prioridade sobre casos raros.
A disciplina de profiling paga aqui: você não está adivinhando qual problema “parece grande”, você escolhe com base em impacto medido.
Aplique mudanças em etapas e prefira segurança
Trabalho de desempenho no ciclo tardio é arriscado porque “correções” podem introduzir novos custos. Use rollouts em etapas: primeiro registre instrumentação, depois ative a mudança atrás de um toggle, depois amplie a exposição. Prefira padrões seguros de desempenho — configurações que protejam o tempo por frame mesmo que reduzam um pouco a qualidade visual — especialmente para configurações auto-detectadas.
Se você lançar múltiplas plataformas ou tiers, trate defaults como uma decisão de produto: é melhor parecer um pouco menos fancy do que parecer instável.
Comunique restrições para stakeholders não técnicos
Traduza trocas em resultados: “Este efeito custa 2 ms por frame em GPUs de nível médio, o que nos arrisca cair abaixo de 60 FPS durante lutas.” Ofereça opções, não palestras: reduzir resolução, simplificar o shader, limitar taxa de spawn, ou aceitar um alvo menor. Restrições são mais fáceis de aceitar quando enquadradas como escolhas concretas com impacto claro no usuário.
Checklist prático para aplicar a mentalidade hoje
Você não precisa de um engine novo ou de um rewrite para adotar pensamento de desempenho ao estilo Carmack. Precisa de um loop repetível que torne desempenho visível, testável e difícil de quebrar acidentalmente.
O loop repetível (medir → orçar → isolar → otimizar → validar → documentar)
-
Medir: capture uma baseline (média, p95, pior spike) para tempo por frame e subsistemas chave.
-
Orçar: defina um orçamento por frame para CPU e GPU (e memória se estiver apertado). Escreva o orçamento ao lado da meta da feature.
-
Isolar: reproduza o custo em uma cena mínima ou teste. Se não conseguir reproduzir, não consegue consertar de forma confiável.
-
Otimizar: mude uma coisa por vez. Prefira mudanças que reduzam trabalho, não apenas “deixem mais rápido”.
-
Validar: re-profile, compare deltas e cheque regressões de qualidade e corretude.
-
Documentar: registre o que mudou, por que ajudou e o que observar no futuro.
Regras práticas que você pode aplicar imediatamente
- Otimizar a barra maior, não o palpite mais irritante.
- Perseguir picos antes de médias se usuários sentirem stutter.
- Se você não consegue explicar o custo, ainda não domina a feature.
- Prefira custos previsíveis a explosões raras no pior caso.
- Orce trabalho novo antecipadamente (ms de CPU, ms de GPU, memória, largura de banda).
- Evite loops por objeto/por frame ocultos que escalam com conteúdo.
- Faça testes de desempenho parte do “feito”, não um aperto pré-lançamento.
Um template simples de “revisão de desempenho” (antes do merge)
- Resumo da feature: o que mudou, o que habilita
- Plataformas & configurações alvo: (ex.: modo perf. de console, PC mid-tier)
- Orçamento: CPU __ ms, GPU __ ms, memória __ MB
- Baseline vs depois: média / ms, p95 / ms, pior spike / ms
- Pressuposição de gargalo: CPU ou GPU? evidência:
- Cena de teste & passos para reproduzir:
- Riscos & guardrails: o que pode regredir, que métricas alertam
- Plano de rollback: como desabilitar ou degradar graciosamente
Onde o Koder.ai entra nesse fluxo
Se você quer operacionalizar esses hábitos numa equipe, a chave é reduzir atrito: experimentos rápidos, harnesses repetíveis e rollbacks fáceis.
Koder.ai pode ajudar quando você está construindo as ferramentas ao redor — não o engine em si. Como plataforma vibe-coding que gera código real exportável (web apps em React; backends em Go com PostgreSQL; mobile em Flutter), você pode rapidamente criar dashboards internos para percentis de tempo por frame, histórico de regressões e checklists de “revisão de desempenho”, então iterar via chat conforme requisitos evoluem. Snapshots e rollback também combinam com o loop “mude uma coisa, re-meça”.
Se quiser mais orientação prática, navegue por /blog ou veja como equipes operacionalizam isso em /pricing.
Perguntas frequentes
Por que o artigo enfatiza tempo de frame (ms) em vez de FPS?
O tempo de frame é o tempo por quadro em milissegundos (ms) e mapeia diretamente quanto trabalho a CPU/GPU fez.
- FPS é recíproco e pode esconder variabilidade.
- O tempo de frame expõe tremulações (por exemplo, frames ocasionais de 40–120 ms) mesmo quando a média de FPS parece aceitável.
- É mais fácil orçar: 16.6 ms = 60 FPS, 33.3 ms = 30 FPS.
Como eu defino um orçamento prático de tempo por frame para meu projeto?
Escolha uma meta (por exemplo, 60 FPS) e converta-a para um prazo rígido (16.6 ms). Em seguida, divida esse prazo em orçamentos explícitos.
Exemplo de ponto de partida:
- CPU: ~7 ms
- GPU: ~9 ms
- Buffer de overhead: ~0.6 ms
Trate esses valores como requisitos de produto e ajuste com base na plataforma, resolução, térmicas e metas de latência de entrada.
Qual é o setup mínimo de profiling que eu devo ter antes de otimizar?
Comece tornando seus testes repetíveis e, só então, meça antes de mudar qualquer coisa.
- Use uma cena fixa + caminho de câmera fixo
- Capture timeline da CPU + timeline da GPU
- Registre contagens auxiliares (draw calls, triângulos, alocações, eventos de streaming)
Só depois de saber onde o tempo está indo você deve decidir o que otimizar.
Como posso dizer rapidamente se estou CPU-bound ou GPU-bound?
Faça experimentos rápidos e focados para isolar o limitador:
- Reduza a resolução: grande melhora geralmente indica que você é limitado por GPU/pixels.
- Desative recursos um por vez (sombras, SSR, AO, partículas): o que mexer significativamente no tempo de frame provavelmente é a sua “grande pedra”.
- Confirme com um profiler de CPU e uma captura de GPU.
Evite reescrever sistemas até conseguir nomear o custo dominante em milissegundos.
Por que os picos de tempo por frame (latência da cauda) são mais importantes que o FPS médio?
Porque os usuários sentem os piores frames, não a média.
Rastreie:
- Percentis (p95/p99/p99.9) para expor a latência da cauda
- Histogramas para ver aglomerações vs outliers
- Correlação de eventos (GC, compilação de shader, carregamento de asset) para atribuir picos
Uma build que tenha média de 16.6 ms mas estoure para 80 ms ainda parecerá quebrada.
Quais são maneiras práticas de reduzir stutter e hitching?
Torne o trabalho caro previsível e agendado:
- Pré-compute (compilações de shader offline, dados pré-assados)
- Aqueça (compile/crie pipelines durante telas de carregamento ou uma cena de warm-up controlada)
- Amortize streaming/descompressão/uploads por muitos frames
- Limite trabalho por frame (por exemplo, “streaming tem no máximo 2 ms este frame”)
Também registre os picos para que você possa reproduzi-los e corrigi-los, em vez de apenas “esperar que sumam”.
Como decido entre qualidade visual, desempenho e complexidade?
Torne a troca explícita em números e impacto no usuário.
Use frases do tipo:
- “Isso adiciona 0.4 ms GPU e 80 MB de VRAM para melhorar a suavidade das sombras.”
Então decida com base em limiares acordados:
- tempo máximo por frame na máquina de referência
- picos aceitáveis no pior caso
- tetos de memória por plataforma
Se estiver em dúvida, prefira decisões reversíveis (feature flags, níveis de qualidade escaláveis).
Por que a correção (correctness) importa tanto para trabalho de desempenho?
Porque correção instável torna os dados de desempenho pouco confiáveis.
Passos práticos:
- Defina invariantes (por exemplo, “cada objeto visível é enviado uma vez”).
- Adicione validações em builds de debug (afirme limites de alocação, valide transições de estado).
- Construa harnesses determinísticos de reprodução (cenas mínimas, entradas roteirizadas).
Se o comportamento mudar de execução para execução, você vai acabar otimizando ruído em vez de gargalos.
O que significa “trabalhar com a máquina” na prática (cache, dados, batching)?
Muito do trabalho “código rápido” é, na verdade, trabalho em memória e overhead.
Foque em:
- Localidade de dados: mantenha dados quentes contíguos para reduzir faltas de cache.
- Controle de alocações: reutilize buffers, use pools, evite churn por frame.
- Batching: reduza draw calls/mudanças de estado/pontos de sincronização antes de micro-otimizar a matemática.
Frequentemente, cortar overhead traz ganhos maiores que ajustar um loop interior.
Como evito regressões de desempenho conforme o projeto evolui?
Torne o desempenho mensurável, repetível e difícil de quebrar acidentalmente.
- Mantenha um pequeno conjunto de cenas base (CPU-heavy, GPU-heavy, pior caso).
- Rode em hardware/config fixa e armazene resultados com hashes de commit.
- Monitore p50/p95/p99 do tempo por frame, memória de pico e tempos de carregamento.
- Defina limiares (por exemplo, p95 não deve regredir mais que 5%).
Quando aparecer uma regressão: bisecte, atribua um dono e reverta rapidamente se bloquear a release.