Alta utilización de la CPU pero bajo promedio de carga

28

Nos encontramos con un comportamiento extraño en el que vemos una alta utilización de la CPU pero un promedio de carga bastante bajo.

El comportamiento se ilustra mejor con los siguientes gráficos de nuestro sistema de monitoreo.

Uso de CPU y carga

Aproximadamente a las 11:57 la utilización de la CPU va del 25% al ​​75%. El promedio de carga no cambia significativamente.

Ejecutamos servidores con 12 núcleos con 2 hiperprocesos cada uno. El sistema operativo ve esto como 24 CPU.

Los datos de utilización de la CPU se recopilan ejecutando /usr/bin/mpstat 60 1cada minuto. Los datos para la allfila y la %usrcolumna se muestran en el cuadro anterior. Estoy seguro de que esto muestra el promedio por datos de CPU, no la utilización "apilada". Si bien vemos un 75% de utilización en el gráfico, vemos un proceso que muestra el uso de aproximadamente 2000% de CPU "apilada" top.

La cifra promedio de carga se toma de /proc/loadavgcada minuto.

uname -a da:

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

Linux dist es Red Hat Enterprise Linux Server release 6.3 (Santiago)

Ejecutamos un par de aplicaciones web Java bajo una carga bastante pesada en las máquinas, piense 100 solicitudes / s por máquina.

Si interpreto los datos de utilización de la CPU correctamente, cuando tenemos un 75% de utilización de la CPU, significa que nuestras CPU están ejecutando un proceso el 75% del tiempo, en promedio. Sin embargo, si nuestras CPU están ocupadas el 75% del tiempo, ¿no deberíamos ver un promedio de carga más alto? ¿Cómo podrían las CPU estar ocupadas al 75% mientras solo tenemos 2-4 trabajos en la cola de ejecución?

¿Estamos interpretando nuestros datos correctamente? ¿Qué puede causar este comportamiento?

K Erlandsson
fuente
¿El sistema de monitoreo muestra una carga de CPU normalizada (carga / #CPU)? La carga normal de CPU de Linux es difícil de comparar entre sistemas con diferentes recuentos de núcleos / CPU, por lo que algunas herramientas utilizan una carga de CPU normalizada.
Brian
¿Te refieres a dividir cada punto de datos con el número de CPU? Es decir loadavg / 24 en nuestro caso? Puedo crear fácilmente un gráfico a partir de los datos si eso ayuda.
K Erlandsson
Estaba sugiriendo que su gráfico ya puede estar mostrando eso.
Brian
Ah, perdón por malinterpretarte. Hubiera sido una buena explicación, pero desafortunadamente se muestra el promedio de carga de todo el sistema. Acabo de verificar tres veces.
K Erlandsson

Respuestas:

51

Al menos en Linux, el promedio de carga y la utilización de CPU son en realidad dos cosas diferentes. El promedio de carga es una medida de cuántas tareas están esperando en una cola de ejecución del núcleo (no solo el tiempo de CPU sino también la actividad del disco) durante un período de tiempo. La utilización de la CPU es una medida de qué tan ocupada está la CPU en este momento. La mayor carga que un solo subproceso de CPU vinculado al 100% durante un minuto puede "contribuir" al promedio de carga de 1 minuto es 1. Una CPU de 4 núcleos con hyperthreading (8 núcleos virtuales) todo al 100% durante 1 minuto contribuiría 8 a El promedio de carga de 1 minuto.

Muchas veces estos dos números tienen patrones que se correlacionan entre sí, pero no puedes pensar en ellos como lo mismo. Puede tener una carga alta con casi un 0% de utilización de la CPU (como cuando tiene muchos datos de E / S atascados en un estado de espera) y puede tener una carga de 1 y 100% de CPU, cuando tiene un proceso único en ejecución inclinación completa Además, durante cortos períodos de tiempo, puede ver la CPU cerca del 100%, pero la carga aún está por debajo de 1 porque las métricas promedio aún no se han "recuperado".

He visto que un servidor tiene una carga de más de 15,000 (sí, realmente no es un error tipográfico) y un CPU% cercano al 0%. Sucedió porque un recurso compartido de Samba tenía problemas y muchos clientes comenzaron a quedarse atascados en un estado de espera de E / S. Lo más probable es que si está viendo un número de alta carga regular sin actividad de CPU correspondiente, está teniendo algún tipo de problema de almacenamiento. En las máquinas virtuales, esto también puede significar que hay otras máquinas virtuales que compiten fuertemente por los recursos de almacenamiento en el mismo host de VM.

La alta carga tampoco es necesariamente algo malo, la mayoría de las veces solo significa que el sistema se está utilizando a su máxima capacidad o tal vez está más allá de su capacidad de mantenerse al día (si el número de carga es mayor que el número de núcleos de procesador). En un lugar donde solía ser un administrador de sistemas, tenían a alguien que observaba el promedio de carga en su sistema primario más de cerca que Nagios. Cuando la carga era alta, me llamaban 24/7 más rápido de lo que se podría decir SMTP. La mayoría de las veces, nada estaba realmente mal, pero asociaron el número de carga con algo que estaba mal y lo vieron como un halcón. Después de verificar, mi respuesta generalmente fue que el sistema solo estaba haciendo su trabajo. Por supuesto, este fue el mismo lugar donde la carga se elevó a más de 15000 (aunque no es el mismo servidor), por lo que a veces significa que algo está mal. Debe considerar el propósito de su sistema. Si es un caballo de batalla, espere que la carga sea naturalmente alta.

deltaray
fuente
¿Cómo quiere decir que puedo tener una carga de 1 y 100% de CPU con un solo proceso de subproceso? ¿De qué tipo de hilos estás hablando? Si consideramos nuestros procesos Java, tienen toneladas de subprocesos, pero suponía que los subprocesos fueron tratados como procesos desde la perspectiva del sistema operativo (después de todo, tienen PID separados en Linux). ¿Podría ser que un solo proceso de Java multihilo solo se cuente como una tarea desde una perspectiva de carga promedio?
K Erlandsson
Acabo de hacer una prueba por mi cuenta, los subprocesos en un proceso Java contribuyen al promedio de carga como si fueran procesos separados (es decir, una clase de Java que ejecuta 10 subprocesos en un bucle de espera ocupada me da una carga cercana a 10). Agradecería una aclaración sobre el proceso roscado que mencionó anteriormente. ¡Gracias!
K Erlandsson
Quiero decir, si tienes un proceso que no es de subprocesos múltiples (es decir, uno que solo usa una sola CPU a la vez). Por ejemplo, si solo escribe un programa C simple que ejecuta un ciclo ocupado, solo se ejecuta un subproceso y usa solo 1 CPU a la vez.
deltaray
Toda la información que he encontrado dice que los hilos cuentan como procesos separados cuando se ven desde el núcleo y al calcular la carga. Por lo tanto, no veo cómo podría tener un proceso de subprocesos múltiples en inclinación completa que resulte en 1 carga y 100% de CPU en un sistema de múltiples CPU. ¿Podrías ayudarme a entender cómo quieres decir?
K Erlandsson
Para cualquiera que busque más detalles: "Promedios de carga de Linux: Resolviendo el misterio" de Brendan Gregg tenía todas las respuestas que necesitaba.
Nickolay
24

La carga es un número muy engañoso. Tómelo con un grano de sal.

Si genera muchas tareas en una sucesión muy rápida que se completa muy rápidamente, el número de procesos en la cola de ejecución es demasiado pequeño para registrar la carga para ellos (el núcleo cuenta la carga una vez cada cinco segundos).

Considere este ejemplo, en mi host que tiene 8 núcleos lógicos, este script de Python registrará un gran uso de CPU en la parte superior (alrededor del 85%), pero casi ninguna 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()

Otra implementación, esta evita waiten grupos de 8 (lo que sesgaría la prueba). Aquí, el padre siempre intenta mantener el número de hijos en el número de CPU activas, por lo que será mucho más ocupado que el primer método y, con suerte, más 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");
  }
}

La razón de este comportamiento es que el algoritmo pasa más tiempo creando procesos secundarios que ejecutando la tarea real (contando hasta 10000). Las tareas aún no creadas no pueden contar para el estado 'ejecutable', pero tomarán% sys en el tiempo de CPU a medida que se generan.

Por lo tanto, la respuesta realmente podría ser en su caso que cualquier trabajo que se esté realizando genera un gran número de tareas en rápida sucesión (hilos o procesos).

Matthew Ife
fuente
Gracias por la sugerencia. El gráfico en mi pregunta muestra el% de tiempo del usuario (el tiempo del sistema de la CPU está excluido, solo vemos un ligero aumento en el tiempo del sistema). ¿Podrían muchas tareas pequeñas ser la explicación de todos modos? Si el promedio de carga se muestrea cada 5 segundos, ¿se muestrean con mayor frecuencia los datos de utilización de la CPU dados por mpstat?
K Erlandsson
No estoy familiarizado con cómo se realiza el muestreo de CPU allí. Nunca lea la fuente del kernel al respecto. En mi ejemplo,% usr fue 70% + y% sys fue 15%.
Matthew Ife
Buenos ejemplos!
Xavier Lucas
5

Si el promedio de carga no aumenta mucho, solo significa que las especificaciones de hardware y la naturaleza de las tareas a procesar dan como resultado un buen rendimiento general, evitando que se acumulen en la cola de tareas durante algún tiempo.

Si hubo un fenómeno de contención porque, por ejemplo, la complejidad promedio de la tarea es demasiado alta o el tiempo promedio de procesamiento de la tarea toma demasiados ciclos de CPU, entonces sí, el promedio de carga aumentaría.

ACTUALIZACIÓN

Puede que no esté claro en mi respuesta original, por lo que estoy aclarando ahora:

La fórmula exacta de cálculo promedio de carga es: loadvg = tasks running + tasks waiting (for cores) + tasks blocked.

Definitivamente puede tener un buen rendimiento y acercarse a un promedio de carga de 24 pero sin penalizar el tiempo de procesamiento de las tareas. Por otro lado, también puede tener entre 2 y 4 tareas periódicas que no se completan con la suficiente rapidez, luego verá crecer el número de tareas en espera (para ciclos de CPU) y eventualmente alcanzará un promedio de carga alto. Otra cosa que puede suceder es que las tareas ejecuten operaciones de E / S síncronas pendientes y luego bloqueen un núcleo, reduzcan el rendimiento y hagan que la cola de tareas de espera crezca (en ese caso, es posible que la iowaitmétrica cambie)

Xavier Lucas
fuente
Entiendo que el promedio de carga también incluye las tareas que se están ejecutando actualmente. Eso significaría que definitivamente podemos tener un aumento en el promedio de carga sin contención real para las CPU. ¿O estoy equivocado / malentendido?
K Erlandsson
@ KristofferE Tienes toda la razón. La fórmula real es loadavg = tareas en ejecución + tareas en espera (para núcleos disponibles) + tareas bloqueadas. Esto significa que puede tener un promedio de carga de 24, sin tareas en espera o bloqueadas, por lo que solo tiene un "uso completo" o la capacidad de su hardware sin ninguna disputa. Como parecía confundido sobre el promedio de carga frente al número de procesos que se ejecutan frente al uso de la CPU, enfoqué mi respuesta principalmente en explicaciones sobre cómo un promedio de carga aún puede crecer con tan pocos procesos en general. Puede que no sea tan claro después de releerlo.
Xavier Lucas
2

El promedio de carga incluye tareas que están bloqueadas en el disco IO, por lo que puede tener una utilización de CPU nula y un promedio de carga de 10 con solo 10 tareas que intentan leer desde un disco muy lento. Por lo tanto, es común que un servidor ocupado comience a sacudir el disco y toda la búsqueda causa muchas tareas bloqueadas, elevando el promedio de carga, mientras que el uso de la CPU cae, ya que todas las tareas están bloqueadas en el disco.

psusi
fuente
1

Si bien la respuesta de Matthew Ife fue muy útil y nos condujo en la dirección correcta, no fue exactamente lo que causó el comportamiento en nuestro caso. En nuestro caso, tenemos una aplicación Java multiproceso que utiliza la agrupación de subprocesos, por lo que no se realiza ningún trabajo para crear las tareas reales.

Sin embargo, el trabajo real que realizan los subprocesos es de corta duración e incluye esperas de E / S o esperas de sincronización. Como Matthew menciona en su respuesta, el sistema operativo muestrea el promedio de carga, por lo que se pueden perder las tareas de corta duración.

Hice un programa Java que reproducía el comportamiento. La siguiente clase Java genera una utilización de CPU del 28% (650% apilada) en uno de nuestros servidores. Al hacer esto, el promedio de carga es de aproximadamente 1.3. La clave aquí es el sueño () dentro del hilo, sin él el cálculo de carga es correcto.

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, la teoría es que los subprocesos en nuestras aplicaciones están inactivos mucho y luego realizan un trabajo de corta duración, por lo que las tareas no se muestrean correctamente mediante el cálculo del promedio de carga.

K Erlandsson
fuente
0

El promedio de carga es el número promedio de procesos en la cola de la CPU. Es específico para cada sistema, no puede decir que una LA es genéricamente alta en todos los sistemas, y otra es baja. Entonces tiene 12 núcleos, y para que LA aumente significativamente, el número de procesos debe ser realmente alto.

Otra pregunta es qué se entiende por el gráfico "Uso de CPU". Si se toma de SNMP, como debería ser, y su implementación de SNMP es net-snmp, entonces solo se apila la carga de CPU de cada una de sus 12 CPU. Entonces, para net-snmpla cantidad total de carga de la CPU es 1200%.

Si mis suposiciones son correctas, entonces el uso de la CPU no aumentó significativamente. Por lo tanto, LA no aumentó significativamente.

drogadicto
fuente
El uso de la CPU se toma de mpstat, la allfila. Estoy bastante seguro de que es un promedio en todas las CPU, no está apilado. Por ejemplo, cuando se produce el problema, top muestra 2000% de uso de CPU para un proceso. Ese es el uso apilado.
K Erlandsson
0

El escenario aquí no es particularmente inesperado, aunque es un poco inusual. Lo que toca Xavier, pero no se desarrolla mucho, es que aunque Linux (por defecto) y la mayoría de los sabores de Unix implementan la multitarea preventiva, en una máquina saludable, las tareas rara vez se adelantan. A cada tarea se le asigna un intervalo de tiempo para ocupar la CPU, solo se adelanta si excede este tiempo y hay otras tareas esperando para ejecutarse (tenga en cuenta que la carga informa el número promedio de procesos tanto en la CPU como en espera de ejecutarse) . La mayoría de las veces, un proceso rendirá en lugar de ser interrumpido.

(en general, solo debe preocuparse por la carga cuando se acerca el número de CPU, es decir, cuando el planificador comienza a adelantar tareas).

Si nuestras CPU están ocupadas el 75% del tiempo, ¿no deberíamos ver un promedio de carga más alto?

Todo se trata del patrón de actividad, la utilización claramente aumentada de la CPU por algunas tareas (muy probablemente una pequeña minoría) no estaba teniendo un efecto adverso en el procesamiento de otras tareas. Si pudiera aislar las transacciones que se están procesando, esperaría que vea un nuevo grupo emergente durante la desaceleración, mientras que el conjunto de tareas existente no se vio afectado.

actualizar

Un escenario común en el que puede ocurrir una CPU alta sin un gran aumento en la carga es donde una tarea desencadena una (o una secuencia) de otras tareas, por ejemplo, al recibir una solicitud de red, el controlador dirige la solicitud a un hilo separado, el hilo separado luego realiza algunas llamadas asincrónicas a otros procesos ... el muestreo de la secuencia de ejecución hace que la carga se informe más baja de lo que realmente es, pero no aumenta linealmente con el uso de la CPU; la cadena de tareas activadas no habría sido ejecutable sin el evento inicial, y debido a que ocurren (más o menos) secuencialmente, la cola de ejecución no se infla.

symcbean
fuente
El OP originalmente proporcionó indicaciones de que el% de CPU agregado era "2000%", lo que sugiere que hay muchas tareas que usan CPU en lugar de solo 1 proceso ocupado. Si fuera un 2000% constante por un minuto, normalmente anticiparía que la carga sería de 20 ish.
Matthew Ife
... en un comentario, no en la pregunta, y él no está muy seguro de eso. En ausencia de la opción 'TODOS', mpstat informa el% de uso total, no el promedio. Pero eso no cambia la respuesta: se trata del patrón de actividad.
symcbean
Estoy 100% seguro de que la utilidad de CPU que vemos en el gráfico es el "promedio por CPU". Mpstat se ejecuta sin ALL, pero eso solo omite la información por CPU, la allfila aún muestra el promedio por CPU. Aclararé la pregunta.
K Erlandsson
¿Podría por favor elaborar un poco su última sección? No entiendo a qué te refieres, mientras que la parte de mi pregunta que mencionaste es la que más me cuesta entender.
K Erlandsson