GNU/Linux >> Znalost Linux >  >> Linux

Vysoké využití procesoru, ale nízké průměrné zatížení

Řešení 1:

Alespoň na Linuxu jsou průměrná zátěž a využití CPU ve skutečnosti dvě různé věci. Průměrná zátěž je měřením toho, kolik úloh čeká ve frontě běhu jádra (nejen čas CPU, ale také aktivita disku) po určitou dobu. Využití procesoru je měřítkem toho, jak je procesor právě teď zaneprázdněn. Největší zátěž, kterou může jedno vlákno procesoru fixované na 100 % po dobu jedné minuty „přispět“ k průměrnému zatížení za 1 minutu, je 1. Čtyřjádrový procesor s hyperthreadingem (8 virtuálních jader) na 100 % po dobu 1 minuty by přispěl 8 k průměrná zátěž za 1 minutu.

Tato dvě čísla mají často vzory, které spolu korelují, ale nemůžete je považovat za stejná. Můžete mít vysoké zatížení s téměř 0% využitím CPU (například když máte mnoho IO dat zaseknutých ve stavu čekání) a můžete mít zatížení 1 a 100% CPU, když běží jeden proces s vlákny. plný náklon. Také na krátkou dobu můžete vidět CPU na úrovni téměř 100 %, ale zatížení je stále pod 1, protože průměrné metriky ještě "nedohnaly".

Viděl jsem, že server má zatížení přes 15 000 (ano, opravdu to není překlep) a CPU % se blíží 0 %. Stalo se to proto, že sdílení Samba mělo problémy a mnoho a mnoho klientů začalo uvíznout ve stavu čekání IO. Je pravděpodobné, že pokud vidíte pravidelně vysoké číslo zatížení bez odpovídající aktivity CPU, máte nějaký problém s úložištěm. Na virtuálních počítačích to může také znamenat, že o prostředky úložiště na stejném hostiteli virtuálního počítače silně soutěží další virtuální počítače.

Vysoká zátěž také nemusí být nutně špatná věc, většinou to jen znamená, že je systém plně využíván nebo je možná mimo jeho schopnost držet krok (pokud je číslo zatížení vyšší než počet jader procesoru). Na místě, kde jsem býval systémovým správcem, měli někoho, kdo sledoval průměr zátěže na jejich primárním systému blíže než Nagios. Když byla zátěž vysoká, volali mi 24/7 rychleji, než byste řekli SMTP. Většinu času se ve skutečnosti nic nedělo, ale spojovali číslo nákladu s tím, že něco není v pořádku, a sledovali to jako jestřáb. Po kontrole jsem obvykle odpověděl, že systém jen dělá svou práci. Samozřejmě to bylo stejné místo, kde zatížení přesáhlo 15 000 (i když ne stejný server), takže to někdy znamená, že je něco špatně. Musíte vzít v úvahu účel vašeho systému. Pokud je to tažný kůň, pak počítejte s tím, že zátěž bude přirozeně vysoká.

Řešení 2:

Náklad je velmi klamavé číslo. Berte to s rezervou.

Pokud vytvoříte mnoho úloh ve velmi rychlém sledu, které se dokončí velmi rychle, počet procesů ve frontě běhu je příliš malý na to, aby pro ně zaregistroval zatížení (jádro počítá zatížení každých pět sekund).

Vezměme si tento příklad, na mém hostiteli, který má 8 logických jader, tento skript pythonu zaznamená velké využití CPU (asi 85 %), ale téměř žádné zatížení.

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

Další implementace, tato se vyhýbá wait ve skupinách po 8 (což by test zkreslilo). Zde se rodič vždy snaží udržet počet dětí na počtu aktivních CPU, takže to bude mnohem vytíženější než první metoda a doufejme, že přesnější.

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

Důvodem tohoto chování je, že algoritmus tráví více času vytvářením podřízených procesů než spuštěním skutečné úlohy (počítáno do 10 000). Úkoly, které ještě nebyly vytvořeny, se nemohou započítávat do stavu 'spustitelný', přesto budou při vytváření zabírat % sys na CPU.

Odpověď by tedy ve vašem případě mohla být skutečně taková, že jakákoliv práce, kterou provádíte, vytváří velké množství úkolů v rychlém sledu (vlákna nebo procesy).

Řešení 3:

Pokud se průměrná zátěž příliš nezvýší, znamená to jen to, že vaše hardwarové specifikace a povaha úloh, které mají být zpracovány, vedou k dobré celkové propustnosti a zabrání tomu, aby se na nějakou dobu hromadily ve frontě úloh.

Pokud by došlo k jevu sporu, protože například průměrná složitost úlohy je příliš vysoká nebo průměrná doba zpracování úlohy trvá příliš mnoho cyklů CPU, pak ano, průměrná zátěž by se zvýšila.

AKTUALIZACE:

V mé původní odpovědi to nemusí být jasné, takže nyní upřesňuji:

Přesný vzorec výpočtu průměrné zátěže je:loadvg = tasks running + tasks waiting (for cores) + tasks blocked .

Rozhodně můžete mít dobrou propustnost a přiblížit se průměrné zátěži 24, ale bez penalizace za dobu zpracování úkolů. Na druhou stranu můžete mít také 2-4 periodické úkoly nedokončené dostatečně rychle, pak uvidíte, že počet čekajících úkolů (na cykly CPU) poroste a nakonec dosáhnete vysokého průměru zatížení. Další věc, která se může stát, je, že na úlohách běží nevyřízené synchronní I/O operace, pak se blokuje jádro, snižuje se propustnost a fronta čekajících úloh se zvětšuje (v takovém případě můžete vidět iowait změna metriky)

Řešení 4:

Zatímco odpověď Matthewa Ifeho byla velmi užitečná a navedla nás správným směrem, v našem případě to nebylo přesně to, co způsobilo chování. V našem případě máme vícevláknovou Java aplikaci, která používá sdružování vláken, proč se nevytváří žádné úkoly.

Skutečná práce, kterou vlákna provádějí, je však krátkodobá a zahrnuje čekání na vstup nebo synchronizaci. Jak Matthew zmiňuje ve své odpovědi, průměrná zátěž je vzorkována operačním systémem, takže krátkodobé úkoly mohou být vynechány.

Vytvořil jsem Java program, který toto chování reprodukoval. Následující třída Java generuje využití CPU 28 % (650 % nahromaděné) na jednom z našich serverů. Přitom je průměrná zátěž asi 1,3. Klíčem je zde sleep() uvnitř vlákna, bez něj je výpočet zatížení správný.

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

}

Abychom to shrnuli, teorie je taková, že vlákna v našich aplikacích jsou hodně nečinná a poté provádějí krátkodobou práci, proč nejsou úlohy správně vzorkovány výpočtem průměrné zátěže.

Řešení 5:

Průměrná zátěž zahrnuje úlohy, které jsou blokovány na IO disku, takže můžete snadno mít nulové využití procesoru a průměrnou zátěž 10 jen tím, že se 10 úloh snaží číst z velmi pomalého disku. Je tedy běžné, že zaneprázdněný server začne mlátit disk a veškeré hledání způsobí spoustu zablokovaných úloh, což zvyšuje průměrnou zátěž, zatímco využití procesoru klesá, protože všechny úlohy jsou na disku blokovány.


Linux
  1. kipmi0 IPMI Kernel Helper 99 % / 100 % vysoké využití CPU (ale nízké průměrné zatížení)

  2. Pochopení průměrného zatížení OS a spuštění fronty/blokované fronty z hlediska využití CPU v Linuxu

  3. Jenkins běží při velmi vysokém využití CPU

  1. Jak zkontrolovat zatížení serveru na serveru Windows

  2. Co znamená průměrná zátěž na Unix/Linux?

  3. Vysoké zatížení systémového procesoru (%sys), systémové zámky

  1. Linux – Jak funguje průměrná zátěž u moderních procesorů?

  2. Vysoký procesor způsobený Sophos ve Windows

  3. Odstraňte problémy s vysokým využitím procesoru na Windows Server