Utilisation élevée du processeur mais faible charge moyenne

28

Nous rencontrons un comportement étrange où nous voyons une utilisation élevée du processeur mais une moyenne de charge assez faible.

Le comportement est mieux illustré par les graphiques suivants de notre système de surveillance.

Utilisation et charge du processeur

Vers 11 h 57, l'utilisation du processeur passe de 25% à 75%. La moyenne de charge n'est pas modifiée de manière significative.

Nous exécutons des serveurs avec 12 cœurs avec 2 hyper threads chacun. Le système d'exploitation voit cela comme 24 CPU.

Les données d'utilisation du processeur sont collectées en exécutant /usr/bin/mpstat 60 1chaque minute. Les données de la allligne et de la %usrcolonne sont présentées dans le graphique ci-dessus. Je suis certain que cela montre la moyenne des données par CPU, pas l'utilisation "empilée". Alors que nous voyons 75% d'utilisation dans le graphique, nous voyons un processus montrant l'utilisation d'environ 2000% de CPU "empilés" top.

Le chiffre de la charge moyenne est tiré de /proc/loadavgchaque minute.

uname -a donne:

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

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

Nous exécutons quelques applications Web Java sous une charge assez lourde sur les machines, pensez à 100 requêtes / s par machine.

Si j'interprète correctement les données d'utilisation du processeur, lorsque nous avons 75% d'utilisation du processeur, cela signifie que nos processeurs exécutent un processus 75% du temps, en moyenne. Cependant, si nos CPU sont occupés 75% du temps, ne devrions-nous pas voir une moyenne de charge plus élevée? Comment les CPU peuvent-ils être occupés à 75% alors que nous n'avons que 2 à 4 tâches dans la file d'attente d'exécution?

Interprétons-nous correctement nos données? Qu'est-ce qui peut provoquer ce comportement?

K Erlandsson
la source
Le système de surveillance affiche-t-il une charge CPU normalisée (charge / #CPU)? La charge régulière du processeur Linux est difficile à comparer entre les systèmes avec différents nombres de cœurs / processeurs, donc certains outils utilisent à la place une charge CPU normalisée.
Brian
Voulez-vous dire en divisant chaque point de données par le nombre de CPU? C'est à dire loadavg / 24 dans notre cas? Je peux facilement créer un tel graphique à partir des données si cela aide.
K Erlandsson
Je suggérais que votre graphique le montre déjà.
Brian
Ah, désolé de vous avoir mal compris. Cela aurait été une bonne explication, mais malheureusement, c'est la moyenne de charge à l'échelle du système qui est affichée. J'ai juste vérifié trois fois.
K Erlandsson

Réponses:

51

Sur Linux au moins, la moyenne de charge et l'utilisation du processeur sont en fait deux choses différentes. La moyenne de charge est une mesure du nombre de tâches en attente dans une file d'attente d'exécution du noyau (pas seulement le temps CPU mais aussi l'activité du disque) sur une période de temps. L'utilisation du processeur est une mesure de l'occupation actuelle du processeur. La charge la plus élevée qu'un thread d'unité centrale fixe à 100% pendant une minute peut «contribuer» à la moyenne de charge d'une minute est 1. Un processeur à 4 cœurs avec hyperthreading (8 cœurs virtuels) tous à 100% pendant 1 minute contribuerait de 8 à la moyenne de charge de 1 minute.

Souvent, ces deux nombres ont des modèles qui sont en corrélation, mais vous ne pouvez pas les considérer comme identiques. Vous pouvez avoir une charge élevée avec près de 0% d'utilisation du processeur (comme lorsque vous avez beaucoup de données d'E / S bloquées dans un état d'attente) et vous pouvez avoir une charge de 1 et 100% du processeur, lorsque vous avez un processus à thread unique en cours d'exécution pleine inclinaison. De plus, pendant de courtes périodes, vous pouvez voir le processeur à près de 100%, mais la charge est toujours inférieure à 1 car les mesures moyennes n'ont pas encore "rattrapé".

J'ai vu un serveur avoir une charge de plus de 15 000 (oui vraiment ce n'est pas une faute de frappe) et un% CPU proche de 0%. Cela s'est produit parce qu'un partage Samba rencontrait des problèmes et de nombreux clients ont commencé à se retrouver bloqués dans un état d'attente d'E / S. Il y a de fortes chances que si vous voyez un nombre de charge élevé régulier sans activité CPU correspondante, vous rencontrez un problème de stockage quelconque. Sur les machines virtuelles, cela peut également signifier que d'autres machines virtuelles sont fortement en concurrence pour les ressources de stockage sur le même hôte de machine virtuelle.

Une charge élevée n'est également pas nécessairement une mauvaise chose, la plupart du temps cela signifie simplement que le système est utilisé à sa pleine capacité ou peut-être au-delà de sa capacité à suivre (si le nombre de charge est supérieur au nombre de cœurs de processeur). À un endroit où j'étais un administrateur système, ils avaient quelqu'un qui regardait la charge moyenne sur leur système principal plus près que Nagios. Lorsque la charge était élevée, ils m'appelaient 24 heures sur 24, 7 jours sur 7 plus rapidement que vous ne pourriez le dire SMTP. La plupart du temps, rien ne se passait vraiment mal, mais ils associaient le numéro de chargement à quelque chose de mal et le regardaient comme un faucon. Après vérification, ma réponse était généralement que le système faisait juste son travail. Bien sûr, c'était le même endroit où la charge a dépassé 15 000 (pas le même serveur cependant), donc cela signifie parfois que quelque chose ne va pas. Vous devez considérer le but de votre système. Si c'est un cheval de bataille, attendez-vous à ce que la charge soit naturellement élevée.

deltaray
la source
Comment voulez-vous dire que je peux avoir une charge de 1 et 100% de CPU avec un seul processus threadé? De quel type de fils parlez-vous? Si nous considérons nos processus Java, ils ont des tonnes de threads, mais j'étais sous l'hypothèse que les threads étaient traités comme des processus du point de vue du système d'exploitation (ils ont des PID distincts sur Linux après tout). Serait-ce possible qu'un seul processus Java multi-thread ne soit compté que comme une tâche du point de vue de la charge moyenne?
K Erlandsson
Je viens de faire un test par moi-même, les threads d'un processus Java contribuent à la moyenne de charge comme s'ils étaient des processus séparés (c'est-à-dire qu'une classe java qui exécute 10 threads dans une boucle d'attente occupée me donne une charge proche de 10). J'apprécierais une clarification sur le processus fileté que vous avez mentionné ci-dessus. Merci!
K Erlandsson
Je veux dire si vous avez un processus non multithreading (c'est-à-dire, qui utilise juste un seul processeur à la fois). Par exemple, si vous écrivez simplement un programme C simple qui exécute une boucle occupée, c'est juste un seul thread qui s'exécute et utilise seulement 1 CPU à la fois.
deltaray
Toutes les informations que j'ai trouvées indiquent que les threads comptent comme des processus séparés lorsqu'ils sont vus depuis le noyau et lors du calcul de la charge. Par conséquent, je ne vois pas comment je pourrais avoir un processus multi-thread sur une inclinaison totale résultant en 1 charge et 100% CPU sur un système multi-CPU. Pourriez-vous s'il vous plaît m'aider à comprendre ce que vous voulez dire?
K Erlandsson
Pour ceux qui recherchent plus de détails: "Moyennes de charge Linux: résoudre le mystère" de Brendan Gregg avait toutes les réponses dont j'avais besoin.
Nickolay
24

La charge est un nombre très trompeur. Prenez-le avec un grain de sel.

Si vous générez de nombreuses tâches en succession très rapide qui se terminent très rapidement, le nombre de processus dans la file d'attente d'exécution est trop petit pour enregistrer la charge pour eux (le noyau compte la charge une fois toutes les cinq secondes).

Considérez cet exemple, sur mon hôte qui a 8 cœurs logiques, ce script python enregistrera une utilisation CPU importante en haut (environ 85%), mais pratiquement aucune charge.

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()

Autre implémentation, celle-ci évite waitpar groupes de 8 (ce qui fausserait le test). Ici, le parent essaie toujours de maintenir le nombre d'enfants au nombre de processeurs actifs, de sorte qu'il sera beaucoup plus occupé que la première méthode et, espérons-le, plus précis.

/* 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 raison de ce comportement est que l'algorithme passe plus de temps à créer des processus enfants qu'à exécuter la tâche réelle (en comptant jusqu'à 10000). Les tâches qui n'ont pas encore été créées ne peuvent pas compter dans l'état «exécutable», mais prendront% sys sur le temps CPU lors de leur apparition.

Ainsi, la réponse pourrait vraiment être dans votre cas que, quel que soit le travail effectué, un grand nombre de tâches se succèdent rapidement (threads ou processus).

Matthew Ife
la source
Merci pour la suggestion. Le graphique dans ma question montre le% de temps utilisateur (le temps système du CPU est exclu, nous ne voyons qu'une très légère augmentation du temps système). De nombreuses petites tâches pourraient-elles être l'explication de toute façon? Si la moyenne de charge est échantillonnée toutes les 5 secondes, les données d'utilisation du processeur fournies par mpstat sont-elles plus fréquemment échantillonnées?
K Erlandsson
Je ne sais pas comment l'échantillonnage du processeur se fait là-bas. Ne lisez jamais la source du noyau à ce sujet. Dans mon exemple,% usr était à 70% + et% sys à 15%.
Matthew Ife
De bons exemples!
Xavier Lucas
5

Si la moyenne de charge n'augmente pas beaucoup, cela signifie simplement que vos spécifications matérielles et la nature des tâches à traiter entraînent un bon débit global, évitant qu'elles ne soient empilées dans la file d'attente des tâches pendant un certain temps.

S'il y avait un phénomène de conflit parce que, par exemple, la complexité moyenne des tâches est trop élevée ou le temps de traitement moyen des tâches prend trop de cycles CPU, alors oui, la moyenne de charge augmenterait.

MISE À JOUR :

Ce n'est peut-être pas clair dans ma réponse d'origine, alors je précise maintenant:

La formule exacte de calcul de la moyenne de la charge est la suivante : loadvg = tasks running + tasks waiting (for cores) + tasks blocked.

Vous pouvez certainement avoir un bon débit et atteindre une moyenne de charge de 24 mais sans pénalité sur le temps de traitement des tâches. D'un autre côté, vous pouvez également avoir 2 à 4 tâches périodiques qui ne se terminent pas assez rapidement, puis vous verrez le nombre de tâches en attente (pour les cycles du processeur) augmenter et vous atteindrez finalement une moyenne de charge élevée. Une autre chose qui peut arriver est que les tâches exécutent des opérations d'E / S synchrones en suspens, puis bloquent un cœur, réduisent le débit et augmentent la file d'attente des tâches en attente (dans ce cas, vous pouvez voir la iowaitmétrique changer)

Xavier Lucas
la source
Je crois comprendre que la moyenne de charge inclut également les tâches en cours d'exécution. Cela signifierait que nous pouvons certainement avoir une augmentation de la charge moyenne sans conflit réel pour les CPU. Ou est-ce que je me trompe / vous méprise?
K Erlandsson
@KristofferE Vous avez tout à fait raison. La formule réelle est loadavg = taks en cours d'exécution + tâches en attente (pour les cœurs disponibles) + tâches bloquées. Cela signifie que vous pouvez avoir une moyenne de charge de 24, aucune tâche en attente ou bloquée, ayant ainsi juste une «pleine utilisation» ou votre capacité matérielle sans contention. Comme vous sembliez confus au sujet de la moyenne de charge par rapport au nombre de processus en cours d'exécution par rapport à l'utilisation du processeur, j'ai principalement concentré ma réponse sur les explications sur la façon dont une moyenne de charge peut encore augmenter avec si peu de processus en cours d'exécution. Ce n'est peut-être pas si clair en effet après l'avoir relu.
Xavier Lucas
2

La moyenne de charge inclut les tâches qui sont bloquées sur le disque IO, vous pouvez donc facilement utiliser zéro processeur et une moyenne de charge de 10 simplement en ayant 10 tâches essayant toutes de lire à partir d'un disque très lent. Ainsi, il est courant qu'un serveur occupé commence à écraser le disque et toutes les recherches provoquent de nombreuses tâches bloquées, augmentant la moyenne de charge, tandis que l'utilisation du processeur diminue, car toutes les tâches sont bloquées sur le disque.

psusi
la source
1

Bien que la réponse de Matthew Ife ait été très utile et nous ait conduits dans la bonne direction, ce n'était pas exactement ce qui a causé le comportement dans notre cas. Dans notre cas, nous avons une application Java multi-thread qui utilise le pool de threads, pourquoi aucun travail n'est fait pour créer les tâches réelles.

Cependant, le travail réel effectué par les threads est de courte durée et inclut les attentes d'E / S ou les attentes de synchronisation. Comme Matthew le mentionne dans sa réponse, la moyenne de charge est échantillonnée par le système d'exploitation, donc des tâches de courte durée peuvent être manquées.

J'ai créé un programme Java qui reproduit le comportement. La classe Java suivante génère une utilisation CPU de 28% (650% empilés) sur l'un de nos serveurs. Ce faisant, la charge moyenne est d'environ 1,3. La clé ici est le sleep () à l'intérieur du thread, sans lui le calcul de la charge est correct.

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();
    }

}

Pour résumer, la théorie est que les threads de nos applications tournent beaucoup au ralenti puis effectuent un travail de courte durée, pourquoi les tâches ne sont pas correctement échantillonnées par le calcul de la moyenne de charge.

K Erlandsson
la source
0

La moyenne de charge est le nombre moyen de processus dans la file d'attente du processeur. Il est spécifique à chaque système, vous ne pouvez pas dire qu'un LA est génériquement élevé sur tous les systèmes et un autre est faible. Vous avez donc 12 cœurs, et pour que LA augmente considérablement, le nombre de processus doit être très élevé.

Une autre question est de savoir ce que l'on entend par le graphique "Utilisation du processeur". Si elle est tirée de SNMP, comme il se doit, et que votre implémentation SNMP l'est net-snmp, alors en piles de charge CPU de chacun de vos 12 CPU. Ainsi, net-snmpla quantité totale de charge CPU est de 1200%.

Si mes hypothèses sont correctes, l'utilisation du processeur n'a pas augmenté de manière significative. Ainsi, LA n'a pas augmenté de manière significative.

drookie
la source
L'utilisation du processeur est tirée de mpstat, la allligne. Je suis assez certain que c'est une moyenne sur tous les processeurs, il n'est pas empilé. Par exemple, lorsque le problème se produit, top affiche 2000% d'utilisation du processeur pour un processus. C'est une utilisation empilée.
K Erlandsson
0

Le scénario ici n'est pas particulièrement inattendu bien qu'il soit un peu inhabituel. Ce que Xavier aborde, mais ne développe pas beaucoup, c'est que bien que Linux (par défaut) et la plupart des versions d'Unix implémentent le multitâche préventif, sur une machine saine, les tâches seront rarement anticipées. Chaque tâche se voit attribuer une tranche de temps pour occuper le processeur, elle n'est préemptée que si elle dépasse ce temps et s'il y a d'autres tâches en attente d'exécution (notez que la charge indique le nombre moyen de processus à la fois dans le processeur et en attente d'exécution) . La plupart du temps, un processus cédera au lieu d'être interrompu.

(en général, vous n'avez à vous soucier de la charge que lorsque le nombre de CPU approche, c'est-à-dire lorsque le planificateur commence les tâches de préemption).

si nos processeurs sont occupés 75% du temps, ne devrions-nous pas voir une moyenne de charge plus élevée?

Tout dépend du modèle d'activité, l'utilisation clairement accrue du processeur par certaines tâches (probablement une petite mintorité) n'a pas eu d'effet négatif sur le traitement d'autres tâches. Si vous pouviez isoler les transactions en cours de traitement, je m'attendrais à voir un nouveau groupe émerger pendant le ralentissement, alors que l'ensemble de tâches existant n'était pas affecté.

mise à jour

Un scénario courant où un processeur élevé peut se produire sans une augmentation importante de la charge est lorsqu'une tâche déclenche une (ou une séquence) d'autres tâches, par exemple à la réception d'une demande réseau, le gestionnaire achemine la demande vers un thread séparé, le thread séparé effectue ensuite des appels asynchrones vers d'autres processus ... l'échantillonnage de la file d'attente entraîne un rapport de la charge inférieur à ce qu'il est réellement - mais il n'augmente pas linéairement avec l'utilisation du processeur - la chaîne de tâches déclenchée n'aurait pas pu être exécutée sans le événement initial, et parce qu'ils se produisent (plus ou moins) séquentiellement, la file d'attente d'exécution n'est pas gonflée.

symcbean
la source
L'OP a à l'origine fourni des indications que le% agrégé du CPU était "2000%", suggérant que de nombreuses tâches utilisent le CPU, plutôt qu'un seul processus occupé. Si c'était 2000% constant pendant une minute, vous vous attendriez normalement à ce que la charge soit de 20 ish.
Matthew Ife
... dans un commentaire, pas dans la question, et il n'en est pas très sûr. En l'absence de l'option 'ALL', mpstat rapporte le% d'utilisation total et non la moyenne. Mais cela ne change pas la réponse - il s'agit du schéma d'activité.
symcbean
Je suis sûr à 100% que l'utilisation du processeur que nous voyons dans le graphique est la "moyenne par processeur". Mpstat est exécuté sans ALL, mais cela ne tient compte que des informations par processeur, la allligne affiche toujours la moyenne par processeur. Je vais clarifier la question.
K Erlandsson
Pourriez-vous s'il vous plaît développer un peu votre dernière section? Je n'arrive pas à comprendre ce que vous voulez dire, alors que la partie de ma question que vous avez citée est celle que j'ai le plus de mal à comprendre.
K Erlandsson