Назад | Перейти на главную страницу

Высокая загрузка ЦП, но низкая средняя нагрузка

Мы сталкиваемся со странным поведением, когда мы видим высокую загрузку процессора, но довольно низкую среднюю нагрузку.

Такое поведение лучше всего иллюстрируют следующие графики из нашей системы мониторинга.

Примерно в 11:57 загрузка процессора увеличивается с 25% до 75%. Средняя загрузка существенно не изменилась.

Мы запускаем серверы с 12 ядрами по 2 гиперпотока на каждом. ОС видит это как 24 процессора.

Данные об использовании ЦП собираются путем запуска /usr/bin/mpstat 60 1 каждую минуту. Данные для all ряд и %usr столбец показан на диаграмме выше. Я уверен, что это показывает среднее значение на данные процессора, не «сложенная» утилизация. В то время как мы видим на диаграмме использование 75%, мы видим процесс, показывающий, что он использует около 2000% «сложенного» ЦП в top.

Среднее значение нагрузки взято из /proc/loadavg каждую минуту.

uname -a дает:

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)

Мы запускаем пару веб-приложений Java при довольно большой нагрузке на машины, думаю, 100 запросов / с на машину.

Если я правильно интерпретирую данные об использовании ЦП, то, когда у нас загрузка ЦП 75%, это означает, что наши ЦП в среднем выполняют процесс 75% времени. Однако, если наши процессоры заняты 75% времени, не должны ли мы увидеть более высокую среднюю нагрузку? Как могут ЦП быть загружены на 75%, если в очереди выполнения всего 2-4 задания?

Правильно ли мы интерпретируем наши данные? Что может вызвать такое поведение?

По крайней мере, в Linux средняя загрузка и загрузка процессора - это две разные вещи. Средняя загрузка - это измерение количества задач, ожидающих в очереди выполнения ядра (не только процессорного времени, но и активности диска) в течение определенного периода времени. Загрузка ЦП - это мера того, насколько он занят прямо сейчас. Наибольшая нагрузка, которую один поток ЦП, привязанный к 100% в течение одной минуты, может «способствовать» средней нагрузке за 1 минуту, составляет 1. 4-ядерный ЦП с гиперпоточностью (8 виртуальных ядер) при 100% в течение 1 минуты будет способствовать 8 к средняя нагрузка за 1 минуту.

Часто эти два числа имеют закономерности, которые коррелируют друг с другом, но вы не можете думать о них как об одном. У вас может быть высокая нагрузка с почти 0% загрузкой ЦП (например, когда у вас есть много данных ввода-вывода, застрявших в состоянии ожидания), и у вас может быть загрузка ЦП от 1 до 100%, когда у вас работает однопоточный процесс. полный тент. Также в течение коротких периодов времени вы можете видеть, что ЦП приближается к 100%, но нагрузка все еще ниже 1, потому что средние показатели еще не «догнали».

Я видел, как сервер загружен более 15000 (да, действительно, это не опечатка), а процент ЦП близок к 0%. Это произошло из-за того, что у общего ресурса Samba возникли проблемы, и множество клиентов начали застревать в состоянии ожидания ввода-вывода. Скорее всего, если вы видите регулярную высокую загрузку без соответствующей активности ЦП, у вас какая-то проблема с хранилищем. На виртуальных машинах это также может означать, что есть другие виртуальные машины, активно конкурирующие за ресурсы хранения на том же хосте виртуальных машин.

Высокая нагрузка также не обязательно является плохой вещью, в большинстве случаев это просто означает, что система используется на полную мощность или, возможно, выходит за рамки ее возможностей (если число нагрузки превышает количество ядер процессора). В месте, где я работал системным администратором, у них был кто-то, кто следил за средней нагрузкой на их основную систему ближе, чем Nagios. Когда нагрузка была высокой, они звонили мне 24/7 быстрее, чем можно было бы сказать SMTP. В большинстве случаев на самом деле ничего не было неправильным, но они связали номер загрузки с чем-то неправильным и наблюдали за ним, как ястреб. После проверки я обычно отвечал, что система просто выполняет свою работу. Конечно, это было то же самое место, где нагрузка превысила 15000 (хотя и не на тот же сервер), поэтому иногда это означает, что что-то не так. Вы должны учитывать цель своей системы. Если это рабочая лошадка, то ожидайте, что нагрузка будет естественно высокой.

Нагрузка - очень обманчивое число. Отнеситесь к этому с недоверием.

Если вы запускаете много задач в очень быстрой последовательности, которые выполняются очень быстро, количество процессов в очереди выполнения слишком мало, чтобы зарегистрировать для них нагрузку (ядро считает нагрузку каждые пять секунд).

Рассмотрим этот пример, на моем хосте с 8 логическими ядрами этот скрипт python зарегистрирует большую загрузку ЦП (около 85%), но практически никакой нагрузки.

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

Другая реализация, эта избегает wait в группах по 8 человек (что исказит тест). Здесь родитель всегда пытается сохранить количество дочерних элементов в количестве активных процессоров, поэтому он будет намного загружен, чем первый метод, и, надеюсь, более точным.

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

Причина такого поведения заключается в том, что алгоритм тратит больше времени на создание дочерних процессов, чем на выполнение самой задачи (считая до 10000). Еще не созданные задачи не могут засчитываться в состояние «работоспособность», но при их создании они будут занимать% sys процессорного времени.

Итак, в вашем случае ответ действительно может заключаться в том, что любая выполняемая работа порождает большое количество задач в быстрой последовательности (потоки или процессы).

Если средняя загрузка не сильно увеличивается, это просто означает, что характеристики вашего оборудования и характер обрабатываемых задач обеспечивают хорошую общую пропускную способность, избегая накопления в очереди задач в течение некоторого времени.

Если бы возник конфликт, потому что, например, средняя сложность задачи слишком высока или среднее время обработки задачи занимает слишком много циклов ЦП, тогда да, средняя нагрузка увеличится.

ОБНОВИТЬ :

В моем первоначальном ответе это может быть неясно, поэтому я уточняю:

Точная формула расчета средней нагрузки: loadvg = tasks running + tasks waiting (for cores) + tasks blocked.

Вы определенно можете иметь хорошую пропускную способность и приблизиться к средней загрузке 24, но без потери времени обработки задач. С другой стороны, у вас также может быть 2-4 периодических задачи, которые не завершаются достаточно быстро, тогда вы увидите, что количество задач, ожидающих (для циклов ЦП), растет, и вы в конечном итоге достигнете высокой средней нагрузки. Еще одна вещь, которая может произойти, - это задачи, выполняющие ожидающие синхронные операции ввода-вывода, а затем блокирование ядра, снижение пропускной способности и увеличение очереди ожидающих задач (в этом случае вы можете увидеть iowait изменение метрики)

Средняя загрузка включает в себя задачи, которые заблокированы при вводе-выводе диска, поэтому вы можете легко получить нулевое использование ЦП и среднюю нагрузку 10, просто имея 10 задач, которые все пытаются читать с очень медленного диска. Таким образом, обычно загруженный сервер начинает загружать диск, и все поиски вызывают множество заблокированных задач, увеличивая среднюю нагрузку, в то время как использование процессора падает, поскольку все задачи заблокированы на диске.

Хотя ответ Мэтью Ифе был очень полезен и вел нас в правильном направлении, в нашем случае это было не совсем то, что вызвало поведение. В нашем случае у нас есть многопоточное Java-приложение, которое использует пул потоков, поэтому работа по созданию реальных задач не выполняется.

Однако фактическая работа, которую выполняют потоки, недолговечна и включает в себя ожидания ввода-вывода или ожидания синхронизации. Как упоминает Мэтью в своем ответе, средняя загрузка выбирается ОС, поэтому краткосрочные задачи могут быть пропущены.

Я сделал программу на Java, которая воспроизводила поведение. Следующий класс Java генерирует загрузку ЦП на 28% (650% в сумме) на одном из наших серверов. При этом средняя загрузка составляет около 1,3. Ключевым моментом здесь является sleep () внутри потока, без него расчет нагрузки будет правильным.

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

}

Подводя итог, теория заключается в том, что потоки в наших приложениях часто простаивают, а затем выполняют непродолжительную работу, поэтому задачи не выбираются правильно при вычислении средней нагрузки.

Средняя загрузка - это среднее количество процессов в очереди ЦП. Он специфичен для каждой системы, нельзя сказать, что один LA в целом высокий для всех систем, а другой низкий. Итак, у вас есть 12 ядер, и для того, чтобы LA значительно увеличился, количество процессов должно быть действительно большим.

Другой вопрос, что подразумевается под графиком «Загрузка процессора». Если он взят из SNMP, как и должно быть, и ваша реализация SNMP net-snmp, то просто стеки загрузки CPU от каждого из ваших 12 CPU. Таким образом, для net-snmp общая загрузка процессора составляет 1200%.

Если мои предположения верны, то загрузка ЦП существенно не увеличилась. Таким образом, LA существенно не увеличился.

Сценарий здесь не является особенно неожиданным, хотя и немного необычным. Ксавьер затрагивает, но не особо развивает, что хотя Linux (по умолчанию) и большинство разновидностей Unix реализуют упреждающую многозадачность, на исправной машине задачи редко будут упреждаемыми. Каждой задаче назначается временной интервал для использования ЦП, он освобождается только в том случае, если он превышает это время и есть другие задачи, ожидающие выполнения (обратите внимание, что загрузка сообщает среднее количество процессов как в ЦП, так и ожидающих выполнения) . В большинстве случаев процесс скорее уступит, чем будет прерван.

(в общем, вам нужно беспокоиться о нагрузке только тогда, когда она приближается к количеству процессоров, то есть когда планировщик запускает упреждающие задачи).

если наши процессоры заняты 75% времени, не должны ли мы увидеть более высокую среднюю нагрузку?

Все дело в структуре активности, явно увеличенное использование ЦП некоторыми задачами (скорее всего, небольшая незначительность) не оказало отрицательного влияния на обработку других задач. Если бы вы могли изолировать обрабатываемые транзакции, я ожидал бы, что вы увидите новую группу, появляющуюся во время замедления, в то время как существующий набор задач не пострадал.

Обновить

Один из распространенных сценариев, когда высокая загрузка ЦП может возникнуть без значительного увеличения нагрузки, - это когда задача запускает одну (или последовательность) других задач, например при получении сетевого запроса обработчик направляет запрос в отдельный поток, затем этот отдельный поток выполняет некоторые асинхронные вызовы другим процессам .... выборка очереди выполнения приводит к тому, что нагрузка становится ниже, чем она есть на самом деле, но это не растет линейно с загрузкой ЦП - цепочка запускаемых задач не могла бы быть запущена без начального события, и поскольку они происходят (более или менее) последовательно, очередь выполнения не увеличивается.

Прежде всего, краткий ответ на вопрос: очевидно, что с 12 до 12:05 процессы, которые были обработаны процессором, заняли больше времени, чем это было раньше.

с 11 до 11:55 каждый процесс ОС занимал 25 мс (например) времени процессора.

с 12 до 12:05 каждый процесс ОС занимал 75 мс.

Вот почему средняя загрузка не изменилась, но использование процессора сильно изменилось.

Длинный ответ: использование процессора и средняя нагрузка описывают состояние двух очень разных существ.

использование процессора описывает состояние процессора

load average не имеет ничего общего с cpu.

Поэтому совершенно неприемлемо, когда средняя нагрузка используется для определения загруженности или простоя процессора.

это похоже на попытку узнать, сколько денег получит человек, через прогноз погоды.

средняя загрузка описывает процессы в ОС Linux, а не состояние процессора

Использование cpu описывает, сколько времени cpu делал что-то вместо того, чтобы ничего не делать в течение некоторого периода времени, скажем для простоты в течение 1 секунды.

если загрузка процессора = 85%. это означает, что 85 мс ЦП был занят, а 15 мс бездействовал. Это оно.

Использование процессора очень похоже на характеристику времени занятости жесткого диска%.

средняя загрузка = 125 в течение 1 секунды означает, что 125 процессов были обработаны процессором или ожидали обработки или ожидали системы hdd.

это сложно, поэтому легко понять, что 125 процессов обрабатывались процессором. Дело в том, что мы не знаем, сколько времени каждый процесс выполнялся на процессоре. мы просто знаем, что они бежали какое-то неизвестное время.

Так что, на мой взгляд, средняя нагрузка создает много путаницы и вреда, когда мы пытаемся понять производительность, а не делать что-то полезное.

когда мы смотрим на исходный график, мы видим, что нет никакой корреляции между использованием процессора и средней нагрузкой в ​​течение всего периода времени. это все равно что пытаться найти корреляцию между прогнозом погоды и цветом вашей чашки чая.