Elevato utilizzo della CPU ma basso carico medio


28

Stiamo riscontrando uno strano comportamento in cui vediamo un elevato utilizzo della CPU ma una media di carico piuttosto bassa.

Il comportamento è meglio illustrato dai seguenti grafici del nostro sistema di monitoraggio.

Utilizzo e caricamento della CPU

Alle 11:57 circa l'utilizzo della CPU va dal 25% al ​​75%. La media del carico non viene modificata in modo significativo.

Eseguiamo server con 12 core con 2 hyper thread ciascuno. Il sistema operativo lo vede come 24 CPU.

I dati di utilizzo della CPU vengono raccolti eseguendo /usr/bin/mpstat 60 1ogni minuto. I dati per la allriga e la %usrcolonna sono mostrati nella tabella sopra. Sono certo che questo mostra la media dei dati della CPU, non l'utilizzo "sovrapposto". Mentre nel grafico vediamo un utilizzo del 75%, vediamo un processo che mostra di usare circa il 2000% di CPU "sovrapposte" top.

Il valore medio del carico viene preso da /proc/loadavgogni 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

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

Eseguiamo un paio di applicazioni Web Java con un carico abbastanza pesante sulle macchine, pensiamo a 100 richieste / s per macchina.

Se interpreto correttamente i dati di utilizzo della CPU, quando abbiamo un utilizzo della CPU del 75% significa che le nostre CPU eseguono un processo il 75% delle volte, in media. Tuttavia, se le nostre CPU sono occupate il 75% delle volte, non dovremmo vedere una media di carico più elevata? Come possono le CPU essere occupate al 75% mentre abbiamo solo 2-4 lavori nella coda di esecuzione?

Stiamo interpretando correttamente i nostri dati? Cosa può causare questo comportamento?


Il sistema di monitoraggio mostra un carico della CPU normalizzato (carico / #CPU)? Il normale carico della CPU Linux è difficile da confrontare tra sistemi con conteggi core / cpu diversi, quindi alcuni strumenti utilizzano invece un carico CPU normalizzato.
Brian,

Intendi dividere ogni punto dati con il numero di CPU? Vale a dire loadavg / 24 nel nostro caso? Posso facilmente creare un tale grafico dai dati se ciò aiuta.
K Erlandsson,

Stavo suggerendo che il tuo grafico potrebbe già mostrarlo.
Brian,

Ah, scusa per averti frainteso. Sarebbe stata una bella spiegazione, ma sfortunatamente è la media del carico a livello di sistema che viene mostrata. Ho appena controllato tre volte.
K Erlandsson,

Risposte:


51

Almeno su Linux, la media del carico e l'utilizzo della CPU sono in realtà due cose diverse. Il carico medio è una misura di quante attività sono in attesa in una coda di esecuzione del kernel (non solo tempo di CPU ma anche attività del disco) per un periodo di tempo. L'utilizzo della CPU è una misura di quanto la CPU è occupata in questo momento. Il maggior carico che un singolo thread della CPU ancorato al 100% per un minuto può "contribuire" alla media del carico di 1 minuto è 1. Una CPU a 4 core con hyperthreading (8 core virtuali) tutto al 100% per 1 minuto contribuirebbe a 8 a la media del carico di 1 minuto.

Spesso questi due numeri hanno schemi correlati tra loro, ma non puoi pensarli come uguali. Puoi avere un carico elevato con quasi lo 0% di utilizzo della CPU (come quando hai molti dati IO bloccati in uno stato di attesa) e puoi avere un carico dell'1 e del 100% della CPU, quando hai in esecuzione un singolo processo con thread inclinazione completa. Inoltre per brevi periodi di tempo puoi vedere la CPU quasi al 100% ma il carico è ancora inferiore a 1 perché le metriche medie non sono ancora "raggiunte".

Ho visto un server avere un carico di oltre 15.000 (sì, davvero non è un errore di battitura) e una CPU% vicino allo 0%. È successo perché una condivisione Samba stava avendo problemi e molti e molti clienti hanno iniziato a rimanere bloccati in uno stato di attesa IO. È probabile che se vedi un numero di carico elevato regolare senza alcuna attività CPU corrispondente, stai riscontrando un problema di archiviazione di qualche tipo. Sulle macchine virtuali ciò può anche significare che ci sono altre macchine virtuali che competono fortemente per le risorse di archiviazione sullo stesso host di macchine virtuali.

Anche il carico elevato non è necessariamente una cosa negativa, il più delle volte significa solo che il sistema viene utilizzato alla sua massima capacità o forse è al di là della sua capacità di tenere il passo (se il numero di carico è superiore al numero di core del processore). In un posto in cui ero un amministratore di sistema, avevano qualcuno che osservava la media del carico sul loro sistema primario più vicino di Nagios. Quando il carico era elevato, mi chiamavano 24/7 più velocemente di quanto si possa dire SMTP. Il più delle volte nulla era effettivamente sbagliato, ma associavano il numero di carico a qualcosa che non andava e lo guardavano come un falco. Dopo il controllo, di solito la mia risposta era che il sistema stava solo facendo il suo lavoro. Ovviamente questo era lo stesso posto in cui il carico ha superato i 15000 (non lo stesso server), quindi a volte significa che qualcosa non va. Devi considerare lo scopo del tuo sistema. Se è un cavallo di battaglia, aspettati che il carico sia naturalmente elevato.


Come intendi che posso avere un carico dell'1 e del 100% della CPU con un singolo processo thread? Di che tipo di discussioni stai parlando? Se consideriamo i nostri processi Java, hanno tonnellate di thread, ma io supponevo che i thread fossero trattati come processi dal punto di vista del sistema operativo (dopo tutto hanno PID separati su Linux). Potrebbe essere che un singolo processo java multi thread sia conteggiato solo come un'attività dal punto di vista della media del carico?
K Erlandsson,

Ho appena fatto un test da solo, i thread in un processo Java contribuiscono alla media del carico come se fossero processi separati (ovvero una classe java che esegue 10 thread in un ciclo di attesa occupato mi dà un carico vicino a 10). Gradirei un chiarimento sul processo filettato che hai menzionato sopra. Grazie!
K Erlandsson,

Voglio dire se hai un processo non multithreading (cioè uno che utilizza solo una singola CPU alla volta). Ad esempio, se si scrive un semplice programma C che esegue un ciclo occupato, è in esecuzione un solo thread e utilizza solo 1 CPU alla volta.
deltaray,

Tutte le informazioni che ho trovato dicono che i thread contano come processi separati quando visti dal kernel e quando si calcola il carico. Quindi non riesco a vedere come potrei avere un processo multi-thread su full tilt con conseguente carico 1 e CPU al 100% su un sistema multi-CPU. Potresti aiutarmi per favore a capire come intendi?
K Erlandsson,

Per chiunque sia alla ricerca di maggiori dettagli: "Linux Load Averages: Solving the Mystery" di Brendan Gregg aveva tutte le risposte di cui avessi mai avuto bisogno.
Nickolay,

24

Il carico è un numero molto ingannevole. Prendilo con un granello di sale.

Se si generano molte attività in successione molto rapida che si completano molto rapidamente, il numero di processi nella coda di esecuzione è troppo piccolo per registrare il carico per loro (il conteggio dei kernel si carica una volta ogni cinque secondi).

Considera questo esempio, sul mio host che ha 8 core logici, questo script Python registrerà un grande utilizzo della CPU in alto (circa l'85%), ma quasi nessun carico.

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

Un'altra implementazione, questa evita waitin gruppi di 8 (che distorcerebbero il test). Qui il genitore cerca sempre di mantenere il numero di figli sul numero di CPU attive in modo tale che sarà molto più occupato del primo metodo e si spera più accurato.

/* 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");
  }
}

Il motivo di questo comportamento è che l'algoritmo impiega più tempo a creare processi figlio rispetto a quando esegue l'attività effettiva (contando fino a 10000). Le attività non ancora create non possono essere conteggiate nello stato "eseguibile", ma impiegheranno% sys sul tempo della CPU man mano che vengono generate.

Quindi, la risposta potrebbe davvero essere nel tuo caso che qualsiasi lavoro venga svolto genera un gran numero di attività in rapida successione (thread o processi).


Grazie per il suggerimento Il grafico nella mia domanda mostra% tempo utente (il tempo di sistema della CPU è escluso, vediamo solo un leggero aumento del tempo di sistema). Molti piccoli compiti potrebbero comunque essere la spiegazione? Se la media del carico viene campionata ogni 5 secondi, i dati di utilizzo della CPU forniti da mpstat vengono campionati più frequentemente?
K Erlandsson,

Non ho familiarità con il modo in cui viene eseguito il campionamento della CPU lì. Non leggere mai il sorgente del kernel al riguardo. Nel mio esempio% usr era del 70% + e% sys era del 15%.
Matthew Ife,

Buoni esempi!
Xavier Lucas,

5

Se la media del carico non aumenta molto, significa solo che le specifiche hardware e la natura delle attività da elaborare producono un buon throughput complessivo, evitando che vengano accumulati nella coda delle attività per qualche tempo.

Se si verificasse un fenomeno di contesa perché, ad esempio, la complessità media dell'attività è troppo elevata o il tempo di elaborazione medio dell'attività richiede troppi cicli della CPU, sì, la media del carico aumenterebbe.

AGGIORNARE :

Potrebbe non essere chiaro nella mia risposta originale, quindi sto chiarendo ora:

La formula esatta di calcolo carico medio è: loadvg = tasks running + tasks waiting (for cores) + tasks blocked.

Puoi sicuramente avere un buon throughput e avvicinarti a una media di carico di 24 ma senza penalità sui tempi di elaborazione delle attività. D'altra parte puoi anche avere 2-4 attività periodiche che non si completano abbastanza rapidamente, quindi vedrai crescere il numero di attività in attesa (per i cicli della CPU) e alla fine raggiungerai una media di carico elevato. Un'altra cosa che può accadere è avere attività che eseguono operazioni di I / O sincrone in sospeso, quindi bloccare un core, ridurre la velocità effettiva e far crescere la coda delle attività di attesa (in tal caso è possibile che la iowaitmetrica cambi)


Comprendo che il carico medio include anche le attività attualmente in esecuzione. Ciò significherebbe che possiamo sicuramente avere un aumento della media del carico senza contese effettive per le CPU. O ti sbaglio / ti fraintendo?
K Erlandsson,

@KristofferE Hai perfettamente ragione. La formula effettiva è loadavg = taks running + task in attesa (per core disponibili) + task bloccati. Questo significa che puoi avere una media di carico di 24, nessuna attività in attesa o bloccata, avendo quindi solo un "pieno utilizzo" o la tua capacità hardware senza alcuna contesa. Dato che sei sembrato confuso riguardo alla media del carico rispetto al numero di processi in esecuzione rispetto all'utilizzo della CPU, ho concentrato principalmente la mia risposta sulle spiegazioni su come una media del carico può ancora crescere con così pochi processi in esecuzione nel complesso. Potrebbe non essere così chiaro dopo averlo riletto.
Xavier Lucas,

2

La media del carico include attività che sono bloccate sull'IO del disco, quindi puoi facilmente avere zero utilizzo della cpu e una media del carico di 10 semplicemente avendo 10 attività tutte che provano a leggere da un disco molto lento. Quindi è comune che un server occupato inizi a bloccare il disco e tutte le ricerche causano molte attività bloccate, aumentando la media del carico, mentre l'utilizzo della cpu diminuisce, poiché tutte le attività sono bloccate sul disco.


1

Mentre la risposta di Matthew Ife è stata molto utile e ci ha portato nella giusta direzione, non è stato esattamente ciò che ha causato il comportamento nel nostro caso. Nel nostro caso abbiamo un'applicazione Java multi-thread che utilizza il pool di thread, perché non viene fatto alcun lavoro per creare le attività effettive.

Tuttavia, il lavoro effettivo svolto dai thread è di breve durata e include le attese IO o le sincronizzazioni. Come menziona Matthew nella sua risposta, la media del carico viene campionata dal sistema operativo, quindi si possono perdere attività di breve durata.

Ho realizzato un programma Java che riproduceva il comportamento. La seguente classe Java genera un utilizzo della CPU del 28% (650% in pila) su uno dei nostri server. Nel fare ciò, la media del carico è di circa 1.3. La chiave qui è sleep () all'interno del thread, senza di essa il calcolo del carico è corretto.

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

}

Per riassumere, la teoria è che i thread nelle nostre applicazioni restano inattivi molto e quindi eseguono lavori di breve durata, perché i compiti non vengono correttamente campionati dal calcolo della media del carico.


0

La media del carico è il numero medio di processi nella coda della CPU. È specifico per ciascun sistema, non si può dire che un LA sia genericamente alto su tutti i sistemi e un altro sia basso. Quindi hai 12 core e affinché LA aumenti in modo significativo il numero di processi deve essere davvero elevato.

Un'altra domanda è cosa si intende per grafico "Utilizzo CPU". Se è preso da SNMP, come dovrebbe essere, e l'implementazione SNMP è net-snmp, quindi impila semplicemente il carico della CPU da ciascuna delle tue 12 CPU. Quindi per net-snmpla quantità totale di carico della CPU è del 1200%.

Se i miei presupposti sono corretti, l'utilizzo della CPU non è aumentato in modo significativo. Pertanto, LA non è aumentata in modo significativo.


L'utilizzo della cpu è preso da mpstat, la allriga. Sono abbastanza sicuro che è una media su tutte le CPU, non è sovrapposta. Ad esempio, quando si verifica il problema, top mostra l'utilizzo della CPU del 2000% per un processo. Questo è un utilizzo in pila.
K Erlandsson,

0

Lo scenario qui non è particolarmente inaspettato anche se è un po 'insolito. Ciò che Xavier tocca, ma non si sviluppa molto, è che sebbene Linux (per impostazione predefinita) e la maggior parte delle versioni di Unix implementino il multi-tasking preventivo, su una macchina sana, le attività raramente verranno anticipate. A ogni attività viene assegnata una fascia oraria per occupare la CPU, viene anticipata solo se supera questo tempo e ci sono altre attività in attesa di esecuzione (si noti che il caricamento riporta il numero medio di processi sia nella CPU che in attesa di esecuzione) . Il più delle volte, un processo produrrà piuttosto che essere interrotto.

(in generale è necessario preoccuparsi del carico solo quando si avvicina il numero di CPU, ovvero quando lo scheduler inizia a svolgere attività preliminari).

se le nostre CPU sono occupate il 75% delle volte, non dovremmo vedere una media di carico più elevata?

Riguarda il modello di attività, chiaramente un aumento dell'utilizzo della CPU da parte di alcune attività (molto probabilmente una piccola coniata) non ha avuto effetti negativi sull'elaborazione di altre attività. Se potessi isolare le transazioni in fase di elaborazione, mi aspetto che durante il rallentamento emergerà un nuovo gruppo, mentre il set di attività esistente non è stato interessato.

aggiornare

Uno scenario comune in cui una CPU elevata può verificarsi senza un grande aumento del carico è quello in cui un'attività attiva una (o una sequenza) di altre attività, ad esempio alla ricezione di una richiesta di rete, il gestore indirizza la richiesta a un thread separato, il thread separato quindi effettua alcune chiamate asincrone ad altri processi .... il campionamento del runqueue fa sì che il carico venga riportato più basso di quanto non sia realmente - ma non aumenta linearmente con l'utilizzo della CPU - la catena di attività avviata non sarebbe stata eseguibile senza il evento iniziale e poiché si verificano (più o meno) in sequenza, la coda di esecuzione non viene gonfiata.


Inizialmente il PO forniva indicazioni sul fatto che la percentuale aggregata della CPU era "2000%", suggerendo che ci sono molte attività che utilizzano CPU, piuttosto che solo 1 processo occupato. Se fosse un consistente 2000% per un minuto, normalmente prevedi che il carico sarà di 20 ish.
Matthew Ife,

... in un commento, non nella domanda, e non ne è molto sicuro. In assenza dell'opzione "TUTTI", mpstat riporta l'utilizzo% totale non la media. Ma ciò non cambia la risposta - riguarda il modello di attività.
symcbean,

Sono sicuro al 100% che l'utilizzo della CPU che vediamo nel grafico sia la "media per CPU". Mpstat viene eseguito senza ALL, ma questo lascia fuori solo le informazioni per CPU, la allriga mostra comunque la media per CPU. Chiarirò la domanda.
K Erlandsson,

Potresti per favore elaborare un po 'la tua ultima sezione? Non riesco a capire cosa intendi, mentre la parte della mia domanda che hai citato è la parte che ho più difficoltà a capire.
K Erlandsson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.