GNU/Linux >> Linux Esercitazione >  >> Linux

Elevato utilizzo della CPU ma basso carico medio

Soluzione 1:

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

Spesso questi due numeri hanno schemi correlati tra loro, ma non puoi pensarli come la stessa cosa. 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 di 1 e 100% CPU, quando hai un processo a thread singolo in esecuzione inclinazione completa. Anche per brevi periodi di tempo puoi vedere la CPU vicino al 100% ma il carico è ancora inferiore a 1 perché le metriche medie non hanno ancora "recuperato".

Ho visto un server avere un carico di oltre 15.000 (sì, davvero non è un errore di battitura) e una percentuale di CPU vicina allo 0%. È successo perché una condivisione Samba stava riscontrando problemi e moltissimi client hanno iniziato a rimanere bloccati in uno stato di attesa IO. È probabile che se vedi un numero di carico elevato regolare senza attività della CPU corrispondente, stai riscontrando un problema di archiviazione di qualche tipo. Sulle macchine virtuali questo può anche significare che ci sono altre macchine virtuali in forte competizione 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 semplicemente che il sistema viene utilizzato al massimo delle sue 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 principale più vicino di quanto facesse Nagios. Quando il carico era alto, mi chiamavano 24 ore su 24, 7 giorni su 7 più velocemente di quanto si possa dire SMTP. La maggior parte delle volte non c'era niente che non andasse, ma associavano il numero del carico a qualcosa che non andava e lo osservavano come un falco. Dopo il controllo, la mia risposta di solito era che il sistema stava solo facendo il suo lavoro. Ovviamente questo era lo stesso posto in cui il carico è salito oltre 15000 (non lo stesso server però), 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.

Soluzione 2:

Il carico è un numero molto ingannevole. Prendilo con le pinze.

Se generi molte attività in successione molto rapida che si completano molto rapidamente, il numero di processi nella coda di esecuzione è troppo piccolo per registrarne il carico (il kernel conta il carico una volta ogni cinque secondi).

Considera questo esempio, sul mio host che ha 8 core logici, questo script python registrerà un elevato utilizzo della CPU nella parte superiore (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 wait in gruppi di 8 (che distorcerebbero il test). Qui il genitore tenta sempre di mantenere il numero di figli al numero di CPU attive in modo che sia molto più occupato del primo metodo e, si spera, più 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");
  }
}

Il motivo di questo comportamento è che l'algoritmo dedica più tempo alla creazione di processi figlio rispetto all'esecuzione dell'attività effettiva (contando fino a 10000). Le attività non ancora create non possono essere conteggiate per lo stato 'eseguibile', tuttavia occuperanno %sys di tempo CPU man mano che vengono generate.

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

Soluzione 3:

Se la media del carico non aumenta molto, significa semplicemente che le tue specifiche hardware e la natura delle attività da elaborare si traducono in un buon throughput complessivo, evitando che vengano accumulate nella coda delle attività per un po' di tempo.

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

AGGIORNAMENTO :

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

La formula esatta per il calcolo della media del carico è: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à sul tempo di elaborazione delle attività. D'altra parte puoi anche avere 2-4 attività periodiche che non vengono completate abbastanza velocemente, quindi vedrai aumentare il numero di attività in attesa (per i cicli della CPU) e alla fine raggiungerai una media di carico elevata. Un'altra cosa che può accadere è avere attività che eseguono operazioni di I/O sincrone in sospeso, quindi bloccare un core, ridurre il throughput e far crescere la coda delle attività in attesa (in tal caso potresti vedere iowait cambio di metrica)

Soluzione 4:

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

Tuttavia, il lavoro effettivo svolto dai thread è di breve durata e include attese di IO o di sincronizzazione. Come menziona Matthew nella sua risposta, la media del carico viene campionata dal sistema operativo, quindi è possibile 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 stack) su uno dei nostri server. Durante questa operazione, la media del carico è di circa 1,3. La chiave qui è lo sleep() all'interno del thread, senza di esso 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 molto inattivi e quindi eseguono un lavoro di breve durata, motivo per cui le attività non vengono campionate correttamente dal calcolo della media del carico.

Soluzione 5:

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


Linux
  1. kipmi0 IPMI Kernel Helper 99% / 100% di utilizzo elevato della CPU (ma basso carico medio)

  2. Comprensione del carico medio del sistema operativo e coda di esecuzione/coda bloccata in termini di utilizzo della CPU in Linux

  3. Jenkins in esecuzione con un utilizzo della CPU molto elevato

  4. Cosa significa carico medio su Unix/Linux?

  5. Carico elevato della CPU di sistema (%sys), blocchi di sistema

Come creare il 100% del carico della CPU su un sistema Linux

Comprendi il carico medio in cPanel.

Utilizzo elevato della CPU - Installazione SQL non completata

Che cos'è la media del carico in Linux?

Come controllare l'utilizzo o l'utilizzo della CPU di Linux

A quanto può arrivare il carico di sistema?