19 pontos por GN⁺ 2025-09-04 | 3 comentários | Compartilhar no WhatsApp
  • Em geral, o limite de desempenho de um servidor é avaliado pela % de uso de CPU em ferramentas de monitoramento como top, mas na prática esse indicador não reflete o desempenho de forma linear
  • Em testes com stress-ng em um ambiente com Ryzen 9 5900X, quando o uso estava em 50%, a carga de trabalho real chegava a 60~100%, mostrando uma grande discrepância em relação ao indicador
  • As principais causas são hyper-threading e turbo boost, já que o compartilhamento de recursos entre núcleos lógicos e a variação da frequência de clock distorcem a métrica
  • Por isso, em vez de usar apenas o uso de CPU, um indicador mais preciso é comparar benchmarks da carga de trabalho realmente processável com a vazão atual
  • Interpretar o uso de CPU de forma linear gera grandes erros na estimativa de desempenho, então é necessário um método baseado em benchmark no planejamento do sistema

Incompatibilidade entre o valor de uso de CPU do servidor e a vazão real

  • Ao operar um servidor, muita gente quer verificar se ele está perto da utilização máxima
  • Em geral, consulta-se o maior valor entre uso de rede, memória e CPU em ferramentas de monitoramento como top
  • Mas, na prática, existe o problema de o valor de uso de CPU e a quantidade de trabalho que pode ser processada não crescerem de forma linear

Ambiente e método de teste

  • Experimento baseado em Ubuntu Desktop + Ryzen 9 5900X (12 núcleos/24 threads)
  • Precision Boost Overdrive (Turbo) ativado
  • Simulação de várias cargas com stress-ng (1~24 workers, 1~100% de uso)
  • Métricas medidas: uso de CPU reportado pelo sistema e volume real de cálculo (Bogo ops)

Resumo dos resultados

  • Carga geral de CPU: com 50% de uso, vazão real de 60~65%
  • Operações inteiras de 64 bits: com 50% de uso, vazão de 65~85%
  • Operações de matriz (Matrix math): com 50% de uso, vazão de 80~100%
    • Na prática, mesmo quando workers adicionais não contribuem para o desempenho, o uso de CPU continua subindo

Análise das causas

  • Hyper-threading

    • Estrutura com 12 núcleos físicos + 12 núcleos lógicos
    • Até 12 workers são alocados de forma ideal nos núcleos físicos, mas acima disso há compartilhamento de núcleos lógicos, o que reduz o desempenho
    • Especialmente em operações SIMD (operações de matriz), não há ganho de desempenho por falta de recursos compartilháveis
  • Turbo boost

    • Em baixa carga, 4.9GHz → em carga total, 4.3GHz, ou seja, queda de 15% no clock
    • Isso causa distorção na fórmula de cálculo do uso de CPU (= busy cycles / total cycles)
      • Como o denominador (número total de ciclos) diminui, o aumento do uso é superestimado em relação ao trabalho real

Implicações

  • Uso de CPU não é um indicador absoluto de desempenho
  • Ao dimensionar capacidade de servidor e prever desempenho:
    • 1. Medir a vazão máxima com benchmark
    • 2. Monitorar a vazão em tempo real
    • 3. Comparar os dois valores para avaliar se está perto do limite de desempenho
  • Como há grande variação conforme a arquitetura da CPU (AMD vs Intel), a eficiência do hyper-threading e o funcionamento do turbo, é necessária análise por processador

Conclusão

  • O uso de CPU é apenas a proporção de ciclos ocupados, e não reflete com precisão o desempenho real de processamento
  • Com uso eficiente, mesmo "50% de uso" pode já representar 80~100% do desempenho máximo
  • Portanto, monitoramento de desempenho e planejamento de sistema devem se concentrar em vazão de trabalho baseada em benchmark, e não no uso de CPU

3 comentários

 
ifmkl 2025-09-07

Em ambientes reais de configuração de HPC, o cluster normalmente é montado com hyper-threading desativado por padrão.

 
GN⁺ 2025-09-04
Comentários no Hacker News
  • Não acho que a utilização de CPU seja uma mentira, no sentido de que ela mede uma quantidade claramente definida, mas as pessoas acabam vendo a diferença entre expectativa e realidade quando tentam extrapolar isso para criar um modelo de capacidade
    Hyperthreading (SMT) e Turbo (escala de clock) são apenas algumas das várias variáveis que causam não linearidade; largura de banda de memória compartilhada entre os núcleos, capacidade de interconexão e cache do processador também são recursos que se “esgotam” conforme a carga aumenta
    No software também há fenômenos, como spinlocks, que podem ter impacto não linear na utilização
    A maioria das métricas de utilização de CPU faz médias longas, de alguns segundos até 1 minuto, mas o que importa para o desempenho de servidores sensíveis à latência acontece em dezenas a centenas de milissegundos
    Por isso, a utilização média em janelas de vários segundos não consegue distinguir padrões em burst de padrões suaves
    Mas a abordagem proposta, isto é, “fazer benchmark de quanto trabalho o servidor consegue executar antes que ocorram erros ou latência inaceitável”, também é inerentemente instável
    Quando se tenta encontrar o ponto em que o servidor começa a ficar instável, as medições ficam muito ruidosas, e pela teoria de filas todo ruído é amplificado perto do ponto crítico
    Além disso, o próprio “volume de trabalho que o servidor está executando agora” costuma ser definido de forma instável (é RPS? cada requisição tem um custo diferente, e o IPC também varia com o tempo)
    No fim, o intervalo de confiança obtido pela abordagem de teste de carga acaba sendo mais ou menos do mesmo nível que construir um modelo empírico com métricas de utilização. A premissa é medir corretamente a utilização

    • Se você souber usar perf ou ftrace, dá para obter métricas de processador muito detalhadas em curto prazo e ver diretamente várias causas, como stalls de CPU por cache miss ou acesso à memória, efeito do scheduler etc.
      Mas a maioria das pessoas, mesmo recebendo métricas como IPC ou taxa de cache hit, não sabe o que fazer com isso
      No fim, o que a maioria realmente quer observar é latência e utilização
      Falando de forma grosseira, na maioria das cargas de trabalho não há muito problema de latência até a utilização de CPU passar de 80%
      Mas acima disso, começa a afetar seriamente a latência da carga de trabalho
      Quanto isso afeta a latência, só medindo a própria carga de trabalho para saber
      O quanto você é sensível à latência também depende da organização e do objetivo, e às vezes otimizar throughput pode ser mais importante
      Se você se importa com latência e throughput ao mesmo tempo, meça os dois e decida por conta própria o trade-off adequado

    • Acho que um dos pontos centrais é justamente que a própria definição de “volume de trabalho” é instável
      Por exemplo, é diferente se estamos falando de um serviço público atendendo requisições reais de usuários ou de uma carga de treinamento de IA no backend calculando dados acumulados com calma
      A minha abordagem é que, no ambiente moderno de CPUs multicore com hyperthreading, em que bursts acabam acontecendo com frequência, 60% de utilização de CPU já caracteriza um servidor como “altamente carregado”
      Se passar de 60% por uma parte significativa do dia, considero adequado dividir o trabalho (principalmente para serviços que respondem a requisições de usuários)
      No passado, esse critério frequentemente levava a pensar em autoscaling na nuvem
      Hoje em dia, dá para usar servidores com mais de 100 núcleos por algo em torno de 30 mil dólares, então a situação ficou mais complexa
      Hoje, eu tenderia a uma abordagem híbrida: provisionar um pouco demais no hardware real e expandir conforme necessário para serviços em nuvem (ou uma nuvem interna baseada em Kubernetes)
      O StackOverflow, nos primeiros anos, operava tráfego enorme com eficiência usando rack dedicado e uplink de 10 Gbps, e hoje isso é ainda mais fácil de fazer
      Em resumo, pelo meu critério, se a carga de CPU se mantiver em 65% por mais de 30 minutos, isso já conta como 100% de uso efetivo e eu concluo que é preciso escalar em breve

    • Em uma keynote recente da IEEE Hot Interconnects, também foi mencionado um caso de tuning de latência do Ultra Ethernet
      Em janelas de 2 ou 5 segundos pode parecer tudo estável, mas quando se olha na escala de 100 ms o fenômeno de frame burst aparece claramente
      Ou seja, se a janela de medição do profiling não combina com a carga real, você pode chegar a uma conclusão errada por falso negativo, e isso acaba piorando o problema

    • Concordo totalmente com o ponto levantado
      É justamente por causa da natureza não linear da utilização de CPU que o próprio valor em % se distancia da realidade
      Em outras palavras, o argumento é que a mentira é a utilização de CPU medida apenas em porcentagem

    • Se duas cargas de trabalho registram ambas 100% de uso de CPU, mas uma delas consome muito mais energia e eleva bastante a temperatura do processador, surge a dúvida: será que, na prática, essa carga não está usando mais recursos de transistores?

  • Mesmo que a utilização de CPU não seja uma métrica perfeita, na prática ela ainda pode ser útil
    Mesmo em experiência simples de trabalho de SRE, para tarefas CPU-bound eu já usei números de utilização de CPU junto com teoria de filas para dimensionar servidores antes de grandes eventos, e mesmo usando um critério de %CPU bem mais conservador do que uma abordagem mais “tradicional”, o resultado saiu muito melhor em custo-benefício
    O ponto principal é: se uma métrica é um pouco imprecisa mas útil no campo, vale usá-la sem se preocupar demais
    Dito isso, eu mantinha a utilização de CPU em produção obrigatoriamente abaixo de 40% para garantir um pouco de headroom em vários cenários
    Faltou, na minha opinião, uma justificativa lógica mais forte do autor para a ideia de “evitar alta utilização por teoria de filas”

    • Acho que mesmo uma métrica meio capenga basta, desde que seja usada adequadamente na prática
      Por exemplo, mesmo usando simplesmente média/máximo de métricas percentílicas por host, ou calculando percentis na agregação final a partir de histogramas por host, na prática a troca entre uma abordagem e outra quase nunca fez diferença relevante na operação real
      Tem gente que se preocupa demais com o que está matematicamente certo ou errado, mas no mundo real isso muitas vezes não muda grande coisa

    • 40% na verdade me parece uma utilização bem folgada

    • Acho que olhar CPU% junto com loadavg ajuda bastante a entender o estado do sistema
      Se o loadavg está alto e o CPU% está baixo, pode ser que o sistema esteja preso em rede ou I/O, ou esperando por system calls
      Nesse caso, olhar só CPU% pode fazer você perder o que realmente está sofrendo

    • Tenho a sensação de já ter ouvido exatamente esse discurso antes
      O que o autor menciona está sendo repetido há décadas em livros de teoria de filas, então é curioso que ele escreva como se tivesse descoberto isso só agora

  • Isso me lembrou o texto de Brendan Gregg, "CPU Utilization is Wrong"
    O ponto central do blog é que a utilização de CPU usa apenas a “situação” de CPU ocupada como métrica, e inclusive considera como busy casos em que a CPU está parada esperando
    E o IPC é uma medida para entender o volume real de trabalho útil escondido dentro desse estado de busy

    • Fico curioso por que pessoas chamadas Brendan parecem ter tanto interesse em problemas de utilização de CPU
      Se houver mais algum Brendan por aí, espero que possa explicar
  • Acho que não se deve contar o desempenho fornecido por hyperthreads como se fosse o dobro
    Na prática, mesmo aproveitando bem hyperthreads, muitas vezes o ganho adicional fica em algo como 15% a 30%. Em compensação, a latência pode dobrar
    Como também é preciso considerar a queda de clock quando a utilização dos núcleos sobe, é importante sempre lembrar que no mundo real isso é não linear
    Só com as informações fornecidas pelo sistema operacional já dá para estimar a utilização com muito mais precisão, levando em conta o efeito de hyperthreads e a queda de clock
    Ir além disso e modelar efeitos como limitação de cache/largura de banda de memória ou stalls de pipeline causando perda de desempenho também não me parece tão difícil

    • Um fator que complica a situação é que a eficiência do hyperthread varia enormemente conforme a arquitetura da CPU e a carga de trabalho
      Por exemplo, implementações da AMD (especialmente as Zen mais recentes) tendem a entregar desempenho muito mais independente do que as da Intel (assumindo que não haja gargalo de largura de banda de memória)

    • Em aplicações memory-bound, costuma ser mais fácil ver scaling melhor com hyperthread
      Em um projeto de renderização do qual participei antes, como era memory-bound, vimos ganho de 60% a 70% de desempenho só com hyperthread

    • Há algum tempo rodei um benchmark simples com POV-Ray num i7-3770K (4C/8T)
      De 1 thread para 2 threads foi exatamente o dobro, de 2 para 4 também dobrou, mas de 4 para 8 houve só uns 15% de ganho
      Talvez num benchmark esquisito que repita artificialmente cache misses desse para tirar desempenho quase dobrado com SMT, mas tenho dúvidas sobre o quanto isso seria realista
      Aliás, estou pensando em rodar de novo o teste com POV-Ray. Faz tanto tempo que até bate saudade

  • Parece que o autor percebeu que o desempenho não cresce linearmente conforme o número de %CPU utilization e, como resultado, concluiu que o próprio %CPU utilization é uma “mentira”
    Mesmo sem hyperthreading e sem clock throttling, e até em casos que parecem ter menos variáveis, como Apple silicon, o scaling não sai exatamente proporcional
    Quando vários núcleos começam a ser usados ao mesmo tempo, gargalos em recursos fora da CPU, como overhead de transferência de dados, frequentemente causam o mesmo tipo de problema

    • O Apple silicon também reduz bastante o clock, especialmente nos modelos sem ventoinha (resfriamento passivo)
  • A terminologia de núcleos usada pelo autor está confusa e fora do padrão
    Ele chama um 5900X de sistema de 24 núcleos, mas na prática é uma estrutura com 12 núcleos físicos e 24 hyperthreads em execução
    Doze são núcleos físicos e os outros doze são threads acopladas a cada núcleo
    Mesmo que haja dois conjuntos de instruction pipeline, as unidades funcionais internas são compartilhadas

    • Alguns anos atrás, tentando explicar hyperthreading para meu irmão mais novo, usei uma analogia que ficou na memória: é como papel higiênico de folha dupla
      Você não consegue separar em 24 unidades independentes, mas é parecido com usar algo de 12 de forma duas vezes mais útil

    • Depois do feedback, corrigi a descrição para 12 núcleos / 24 threads
      Mas o meu sistema operacional mostrava a utilização como se fossem 24 núcleos, então isso acabou me confundindo

    • Seria interessante se a Intel lançasse núcleos definidos por software
      Seria o oposto lógico do hyperthreading: uma estrutura em que dois ou mais núcleos compartilham recursos e se convertem em “um grande núcleo”
      Veja a patente no link abaixo
      https://patents.google.com/patent/EP4579444A1/en

    • Quando um par SMT executa o mesmo tipo de carga de trabalho, o efeito do SMT cai por causa da disputa por recursos internos e unidades de execução
      Se as workloads forem totalmente diferentes, o boost pode até ser maior
      Hoje a situação fica ainda mais complexa com variáveis como P-cores, E-cores, turbo/não turbo nos CPUs modernos
      Houve um estudo mostrando que adicionar SMT trazia mais ganho de desempenho por watt do que adicionar turbo, o que achei muito interessante

  • Identificação total com isso
    Uma vez, no passado, o CPU do servidor chegou a 60% e eu expliquei para meu gerente que já não havia mais folga, e ele ficou me olhando com uma expressão estranha
    Naquele momento, eu queria muito ter uma explicação como esta

    • Explicar junto a teoria de filas costuma funcionar muito bem
      Abaixo de 60%, o atraso de enfileiramento é praticamente desprezível
      Ao passar de 70%, a latência começa a ficar claramente perceptível, e a partir de 80% quase dobra
      Na minha experiência real, eu mirava 65% com base em tempo P95, e isso quase batia com o critério teórico
      Em resumo, a regra prática é: “60% é o limite de uso, a partir de 80% a latência explode”

    • Mas isso pode mudar completamente dependendo da carga de trabalho
      Principalmente hoje, numa era em que CPUs de servidor têm 32 núcleos

  • Muitas vezes a utilização de CPU não se comporta como esperado
    Normalmente, num texto desse tipo, eu esperaria uma discussão sobre como o valor de Utilization é calculado no Linux/Windows, mas na prática também há muitos casos em que a RAM é o gargalo e a CPU fica ociosa esperando ou reduzindo clock
    Na prática, a utilização de CPU é apenas uma medida de quanto o sistema operacional (seja Windows ou Linux) atribui threads a cada núcleo
    Mas, se essa thread estiver 100% parada dentro de um memcpy, isso ainda entra como utilização
    A vantagem do hyperthreading é que, se uma thread estiver presa na unidade AVX/vetorial e outra estiver presa em memcpy/RAM, dá para aumentar a utilização de cada unidade e elevar o desempenho total e a utilização
    No fim, CPU Utilization é um tema intuitivamente difícil de entender há muito tempo, e sempre surgem novas perspectivas
    Ainda assim, é sempre um assunto interessante

  • A verdadeira “mentira” é acreditar que um núcleo com hyperthread se comporta igual a um núcleo de verdade
    No fim, depois de usar isso por mais de 20 anos, a gente esquece a natureza original da coisa e volta a se surpreender repetidamente com por que as medições de desempenho parecem estranhas
    Outra coisa é que, no fundo, o processador só está “executando” (100%) ou “parado” (0%)
    Atribuir uma porcentagem intermediária por si só nada mais é do que uma média em alguma unidade de tempo específica

  • Já tive uma conversa assim no passado
    Gerente: como a utilização de CPU está em 100%, não deveríamos trocar o servidor por uma instância maior?
    Eu: “mas a CPU está realmente fazendo trabalho útil agora?”
    No fim, como busy waiting também entra como utilização de CPU, às vezes o número sobe sem relação com trabalho útil de verdade

 
jjjajh 2025-09-04

É a expressão correta.