Alta utilização da CPU, mas baixa carga média

28

Estamos enfrentando um comportamento estranho, em que vemos alta utilização da CPU, mas média de carga bastante baixa.

O comportamento é melhor ilustrado pelos gráficos a seguir do nosso sistema de monitoramento.

Uso e carga da CPU

Por volta das 11:57, a utilização da CPU varia de 25% a 75%. A média de carga não é alterada significativamente.

Executamos servidores com 12 núcleos com 2 hiper threads cada. O sistema operacional vê isso como 24 CPUs.

Os dados de utilização da CPU são coletados executando /usr/bin/mpstat 60 1cada minuto. Os dados para a alllinha e a %usrcoluna são mostrados no gráfico acima. Estou certo de que isso mostra a média por dados da CPU, não a utilização "empilhada". Enquanto observamos 75% de utilização no gráfico, vemos um processo mostrando o uso de cerca de 2000% da CPU "empilhada" top.

O valor médio da carga é obtido a /proc/loadavgcada minuto.

uname -a dá:

Linux ab04 2.6.32-279.el6.x86_64 #1 SMP Wed Jun 13 18:24:36 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux

Dist Linux é Red Hat Enterprise Linux Server release 6.3 (Santiago)

Executamos um par de aplicativos da Web Java com uma carga bastante pesada nas máquinas, pensamos em 100 solicitações / s por máquina.

Se eu interpretar os dados de utilização da CPU corretamente, quando tivermos 75% de utilização da CPU, isso significa que nossas CPUs estão executando um processo 75% do tempo, em média. No entanto, se nossas CPUs estiverem ocupadas 75% das vezes, não devemos ver uma média de carga mais alta? Como as CPUs podem estar 75% ocupadas enquanto temos apenas 2 a 4 trabalhos na fila de execução?

Estamos interpretando nossos dados corretamente? O que pode causar esse comportamento?

K Erlandsson
fonte
O sistema de monitoramento está mostrando a carga normalizada da CPU (carga / #CPUs)? É difícil comparar a carga regular da CPU do Linux nos sistemas com diferentes contagens de núcleo / CPU, portanto, algumas ferramentas usam uma carga normalizada da CPU.
Brian
Você quer dizer dividir cada ponto de dados com o número de CPUs? Ou seja, loadavg / 24 no nosso caso? Posso facilmente criar um gráfico desse tipo a partir dos dados, se isso ajudar.
K Erlandsson
Eu estava sugerindo que seu gráfico já pode estar mostrando isso.
Brian
Ah, desculpe por ter entendido errado. Seria uma boa explicação, mas infelizmente é a média de carga em todo o sistema que é mostrada. Eu apenas verifiquei três vezes.
K Erlandsson

Respostas:

51

No Linux, pelo menos, a média de carga e a utilização da CPU são na verdade duas coisas diferentes. A média de carga é uma medida de quantas tarefas estão aguardando em uma fila de execução do kernel (não apenas o tempo da CPU, mas também a atividade do disco) durante um período de tempo. A utilização da CPU é uma medida de quão ocupada a CPU está no momento. A maior carga que um único encadeamento de CPU atrelado a 100% por um minuto pode "contribuir" para a média de carga de 1 minuto é 1. Uma CPU de 4 núcleos com hyperthreading (8 núcleos virtuais) todos a 100% por 1 minuto contribuiria 8 para a média de carga de 1 minuto.

Muitas vezes, esses dois números têm padrões que se correlacionam, mas você não pode pensar neles como o mesmo. Você pode ter uma carga alta com quase 0% de utilização da CPU (como quando você tem muitos dados de E / S bloqueados no estado de espera) e pode ter uma carga de 1 e 100% da CPU, quando você tem um único processo encadeado em execução inclinação total. Também por curtos períodos de tempo, você pode ver a CPU perto de 100%, mas a carga ainda está abaixo de 1 porque as métricas médias ainda não "alcançaram".

Vi um servidor ter uma carga de mais de 15.000 (sim, na verdade isso não é um erro de digitação) e uma porcentagem de CPU de quase 0%. Isso aconteceu porque um compartilhamento do Samba estava com problemas e muitos clientes começaram a ficar presos em um estado de espera de E / S. As chances são de que, se você estiver vendo um número regular de carga alta sem nenhuma atividade de CPU correspondente, esteja tendo algum tipo de problema de armazenamento. Nas máquinas virtuais, isso também pode significar que há outras VMs competindo fortemente por recursos de armazenamento no mesmo host da VM.

Carga alta também não é necessariamente uma coisa ruim, na maioria das vezes significa apenas que o sistema está sendo utilizado em sua capacidade máxima ou talvez esteja além da capacidade de mantê-lo (se o número de carga for maior que o número de núcleos do processador). Em um lugar que eu costumava ser um administrador de sistemas, eles tinham alguém que observava a carga média em seu sistema primário mais próxima do que Nagios. Quando a carga era alta, eles me chamavam 24/7 mais rápido do que você poderia dizer SMTP. Na maioria das vezes, nada estava realmente errado, mas eles associaram o número de carga a algo errado e o observaram como um falcão. Após a verificação, minha resposta foi geralmente que o sistema estava apenas fazendo seu trabalho. É claro que este foi o mesmo local em que a carga aumentou mais de 15000 (embora não seja o mesmo servidor), então às vezes isso significa que algo está errado. Você deve considerar o objetivo do seu sistema. Se for um cavalo de batalha, espere que a carga seja naturalmente alta.

deltaray
fonte
Como você quer dizer que eu posso ter uma carga de 1 e 100% da CPU com um único processo encadeado? De que tipo de tópicos você está falando? Se considerarmos nossos processos Java, eles têm toneladas de encadeamentos, mas eu assumi que os encadeamentos eram tratados como processos da perspectiva do sistema operacional (eles têm PIDs separados no Linux, afinal). Seria possível que um único processo java multiencadeado fosse contado apenas como uma tarefa de uma perspectiva de média de carga?
K Erlandsson
Acabei de fazer um teste sozinho, os encadeamentos em um processo Java contribuem para a média de carga como se fossem processos separados (ou seja, uma classe java que executa 10 encadeamentos em um loop de espera ocupada me dá uma carga próxima a 10). Gostaria de receber um esclarecimento sobre o processo encadeado que você mencionou acima. Obrigado!
K Erlandsson
Quero dizer, se você tiver um processo não multithreading (ou seja, um que use apenas uma única CPU de cada vez). Por exemplo, se você acabou de escrever um programa C simples que executa um loop ocupado, é apenas um único thread em execução e usa apenas 1 CPU por vez.
Deltaray 13/02/2015
Todas as informações que encontrei dizem que os threads contam como processos separados quando vistos no kernel e ao calcular a carga. Portanto, não consigo ver como eu poderia ter um processo multiencadeado em full tilt, resultando em 1 carregamento e 100% da CPU em um sistema com várias CPUs. Você poderia me ajudar a entender como você quer dizer?
K Erlandsson
Para quem procura mais detalhes: "Médias de Carga do Linux: Solucionando o Mistério", de Brendan Gregg, tinha todas as respostas que eu sempre precisei.
Nickolay #
24

Carga é um número muito enganador. Tome com um grão de sal.

Se você gerar muitas tarefas em sucessão muito rápida e concluídas muito rapidamente, o número de processos na fila de execução é muito pequeno para registrar a carga para eles (o kernel conta a carga uma vez a cada cinco segundos).

Considere este exemplo: no meu host, que possui 8 núcleos lógicos, esse script python registrará um grande uso da CPU no topo (cerca de 85%), mas quase nenhuma carga.

import os, sys

while True:
  for j in range(8):
    parent = os.fork()
    if not parent:
      n = 0
      for i in range(10000):
        n += 1
      sys.exit(0)
  for j in range(8):
    os.wait()

Outra implementação, esta evita waitem grupos de 8 (o que distorceria o teste). Aqui, o pai sempre tenta manter o número de filhos no número de CPUs ativas, de modo que seja muito mais ocupado que o primeiro método e, esperançosamente, mais preciso.

/* Compile with flags -O0 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <err.h>
#include <errno.h>

#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define ITERATIONS 50000

int maxchild = 0;
volatile int numspawned = 0;

void childhandle(
    int signal)
{
  int stat;
  /* Handle all exited children, until none are left to handle */
  while (waitpid(-1, &stat, WNOHANG) > 0) {
    numspawned--;
  }
}

/* Stupid task for our children to do */
void do_task(
    void)
{
  int i,j;
  for (i=0; i < ITERATIONS; i++)
    j++;
  exit(0);
}

int main() {
  pid_t pid;

  struct sigaction act;
  sigset_t sigs, old;

  maxchild = sysconf(_SC_NPROCESSORS_ONLN);

  /* Setup child handler */
  memset(&act, 0, sizeof(act));
  act.sa_handler = childhandle;
  if (sigaction(SIGCHLD, &act, NULL) < 0)
    err(EXIT_FAILURE, "sigaction");

  /* Defer the sigchild signal */
  sigemptyset(&sigs);
  sigaddset(&sigs, SIGCHLD);
  if (sigprocmask(SIG_BLOCK, &sigs, &old) < 0)
    err(EXIT_FAILURE, "sigprocmask");

  /* Create processes, where our maxchild value is not met */
  while (1) {
    while (numspawned < maxchild) {
      pid = fork();
      if (pid < 0)
        err(EXIT_FAILURE, "fork");

      else if (pid == 0) /* child process */
        do_task();
      else               /* parent */
        numspawned++;
    }
    /* Atomically unblocks signal, handler then picks it up, reblocks on finish */
    if (sigsuspend(&old) < 0 && errno != EINTR)
      err(EXIT_FAILURE, "sigsuspend");
  }
}

A razão para esse comportamento é que o algoritmo gasta mais tempo criando processos filhos do que executando a tarefa real (contando até 10000). As tarefas ainda não criadas não podem contar para o estado 'executável', mas ocuparão% sys no tempo da CPU à medida que são geradas.

Portanto, a resposta poderia realmente estar no seu caso: qualquer que seja o trabalho que está sendo realizado, gera um grande número de tarefas em rápida sucessão (threads ou processos).

Matthew Ife
fonte
Obrigado pela sugestão. O gráfico na minha pergunta mostra% de tempo do usuário (a hora do sistema da CPU é excluída, vemos apenas um aumento muito pequeno na hora do sistema). De qualquer forma, muitas pequenas tarefas poderiam ser a explicação? Se a média de carga é amostrada a cada 5 segundos, os dados de utilização da CPU, conforme fornecidos pelo mpstat, são amostrados com mais frequência?
K Erlandsson
Não estou familiarizado com como a amostragem da CPU é feita lá. Nunca leia a fonte do kernel sobre isso. No meu exemplo,% usr foi 70% + e% sys foi 15%.
Matthew Ife
Bons exemplos!
Xavier Lucas
5

Se a média de carga não aumentar muito, significa que as especificações de seu hardware e a natureza das tarefas a serem processadas resultam em um bom rendimento geral, evitando que elas sejam empilhadas na fila de tarefas por algum tempo.

Se houve um fenômeno de contenção porque, por exemplo, a complexidade média da tarefa é muito alta ou o tempo médio de processamento da tarefa leva muitos ciclos de CPU, então sim, a média da carga aumentaria.

ATUALIZAÇÃO:

Pode não estar claro na minha resposta original, então estou esclarecendo agora:

A fórmula de cálculo exacto médio de carregamento é: loadvg = tasks running + tasks waiting (for cores) + tasks blocked.

Você pode definitivamente ter uma boa taxa de transferência e chegar perto de uma carga média de 24, mas sem penalizar o tempo de processamento das tarefas. Por outro lado, você também pode ter de 2 a 4 tarefas periódicas não concluindo com rapidez suficiente, e você verá o número de tarefas em espera (por ciclos de CPU) crescendo e, eventualmente, atingirá uma alta média de carga. Outra coisa que pode acontecer é ter tarefas executando excelentes operações de E / S síncrona, bloqueando um núcleo, reduzindo a taxa de transferência e aumentando a fila de tarefas em espera (nesse caso, a iowaitmétrica pode ser alterada)

Xavier Lucas
fonte
Entendo que a média de carga também inclui as tarefas atualmente em execução. Isso significaria que definitivamente podemos ter um aumento na média de carga sem contenção real para as CPUs. Ou estou enganado / entendendo mal você?
K Erlandsson
@KristofferE Você está completamente certo. A fórmula real é loadavg = tarefas em execução + tarefas em espera (por núcleos disponíveis) + tarefas bloqueadas. Isso significa que você pode ter uma carga média de 24, nenhuma tarefa aguardando ou bloqueada, tendo assim apenas um "uso completo" ou a capacidade do seu hardware sem qualquer contenção. Como você parecia confuso sobre a média de carga versus o número de processos em execução versus o uso da CPU, concentrei minha resposta principalmente em explicações sobre como uma média de carga ainda pode crescer com tão poucos processos em execução no geral. Pode não ser tão claro assim depois de relê-lo.
Xavier Lucas
2

A média de carga inclui tarefas bloqueadas no IO do disco, para que você possa facilmente ter zero utilização da CPU e uma média de carga de 10 apenas com 10 tarefas tentando ler de um disco muito lento. Portanto, é comum que um servidor ocupado comece a debulhar o disco e todas as buscas causem muitas tarefas bloqueadas, aumentando a média de carga, enquanto o uso da CPU diminui, pois todas as tarefas estão bloqueadas no disco.

psusi
fonte
1

Embora a resposta de Matthew Ife tenha sido muito útil e nos tenha levado na direção certa, não foi exatamente isso que causou o comportamento no nosso caso. No nosso caso, temos um aplicativo Java multiencadeado que usa pool de encadeamentos, por que nenhum trabalho é feito na criação das tarefas reais.

No entanto, o trabalho real dos encadeamentos é de curta duração e inclui esperas de E / S ou de sincronização. Como Matthew menciona em sua resposta, a média de carga é amostrada pelo sistema operacional, portanto, tarefas de curta duração podem ser perdidas.

Eu criei um programa Java que reproduzia o comportamento. A classe Java a seguir gera uma utilização da CPU de 28% (650% empilhada) em um de nossos servidores. Enquanto isso, a média de carga é de cerca de 1,3. A chave aqui é o sleep () dentro do encadeamento, sem ele o cálculo da carga está correto.

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MultiThreadLoad {

    private ThreadPoolExecutor e = new ThreadPoolExecutor(200, 200, 0l, TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy());

    public void load() {
        while (true) {
            e.execute(new Runnable() {

                @Override
                public void run() {
                    sleep100Ms();
                    for (long i = 0; i < 5000000l; i++)
                        ;
                }

                private void sleep100Ms() {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }

    public static void main(String[] args) {
        new MultiThreadLoad().load();
    }

}

Para resumir, a teoria é que os encadeamentos em nossos aplicativos ficam ociosos e, em seguida, executam trabalhos de curta duração, por que as tarefas não são amostradas corretamente pelo cálculo da média de carga.

K Erlandsson
fonte
0

Load average é o número médio de processos na fila da CPU. É específico para cada sistema, você não pode dizer que um LA é genericamente alto em todos os sistemas e outro é baixo. Então você tem 12 núcleos e, para LA aumentar significativamente, o número de processos deve ser realmente alto.

Outra pergunta é o que significa o gráfico "Uso da CPU". Se for retirado do SNMP, como deveria ser, e sua implementação SNMP for net-snmp, apenas empilha a carga da CPU de cada uma das 12 CPUs. Portanto, net-snmpa quantidade total de carga da CPU é de 1200%.

Se minhas suposições estiverem corretas, o uso da CPU não aumentou significativamente. Assim, LA não aumentou significativamente.

drookie
fonte
O uso da CPU é obtido no mpstat, a alllinha. Estou bastante certo de que é uma média em todas as CPUs, não está empilhado. Por exemplo, quando o problema ocorre, o topo mostra 2000% de uso da CPU para um processo. Esse é o uso empilhado.
K Erlandsson
0

O cenário aqui não é particularmente inesperado, embora seja um pouco incomum. O que Xavier aborda, mas não desenvolve muito, é que, embora o Linux (por padrão) e a maioria dos tipos de Unix implementem multitarefas preventivas, em uma máquina saudável, as tarefas raramente serão antecipadas. Cada tarefa recebe um intervalo de tempo para ocupar a CPU; ela é antecipada apenas se exceder esse tempo e houver outras tarefas aguardando para serem executadas (observe que load informa o número médio de processos na CPU e aguardando para executar) . Na maioria das vezes, um processo renderá em vez de ser interrompido.

(em geral, você só precisa se preocupar com a carga quando se aproxima o número de CPUs - ou seja, quando o agendador começa a antecipar tarefas).

se nossas CPUs estiverem ocupadas 75% do tempo, não devemos ver uma média de carga mais alta?

Tudo sobre o padrão de atividade, claramente aumentou a utilização da CPU em algumas tarefas (provavelmente uma pequena minoria), não tendo um efeito adverso no processamento de outras tarefas. Se você pudesse isolar as transações sendo processadas, esperaria que surgisse um novo grupo durante a desaceleração, enquanto o conjunto de tarefas existente não foi afetado.

atualizar

Um cenário comum em que a alta CPU pode ocorrer sem um grande aumento de carga é quando uma tarefa dispara uma (ou uma sequência) de outras tarefas, por exemplo, ao receber uma solicitação de rede, o manipulador direciona a solicitação para um encadeamento separado, o encadeamento separado em seguida, faz algumas chamadas assíncronas para outros processos .... a amostragem da fila de execução faz com que a carga seja menor do que realmente é - mas não aumenta linearmente com o uso da CPU - a cadeia de tarefas acionadas não seria executável sem o evento inicial e, como ocorrem (mais ou menos) seqüencialmente, a fila de execução não é inflada.

symcbean
fonte
O OP originalmente forneceu indicações de que a% agregada da CPU era "2000%", sugerindo que há muitas tarefas usando a CPU, em vez de apenas um processo ocupado. Se fosse um consistente 2000% por um minuto, você normalmente anteciparia que a carga seria 20-ish.
Matthew Ife
... em um comentário, não na pergunta, e ele não tem muita certeza disso. Na ausência da opção 'ALL', o mpstat relata o% total de uso e não a média. Mas isso não muda a resposta - é sobre o padrão de atividade.
symcbean
Tenho 100% de certeza de que o utilitário de CPU que vemos no gráfico é a "média por CPU". O Mpstat é executado sem ALL, mas isso exclui apenas as informações por CPU, a alllinha ainda mostra a média por CPU. Vou esclarecer a questão.
K Erlandsson
Você poderia elaborar um pouco sua última seção? Não consigo entender o que você quer dizer, enquanto a parte da minha pergunta que você citou é a parte que tenho mais dificuldade para entender.
K Erlandsson