Як вважається Load Average

Постановка питання
Нещодавно, під час співбесіди в одну велику компанію мені поставили просте запитання, що таке Load Average. Не знаю, на скільки правильно я відповів, але особисто для себе прийшло усвідомлення, що точної відповіді я насправді не знаю.
Більшість людей, напевно, знають, що Load Average — це середнє завантаження системи за деякий період часу (1,5 і 15 хвилин). Так само можна дізнатися деякі подробиці з даній статті, про те, як цим користуватися. У більшості випадків цих знань достатньо для того, що б за значенням LA оцінювати завантаження системи, але я за фахом фізик, і коли я бачу «середнє за проміжок часу» мені відразу стає цікава частота дискретизації на даному проміжку. А коли я бачу термін «очікують ресурсів», стає цікаво, яких саме і скільки часу треба чекати, а так само скільки тривіальних процесів треба запустити, що б отримати за короткий проміжок часу високий LA. І головне, чому відповіді на ці питання не дає 5 хвилин роботи з гуглом? Якщо вам дані тонкощі так само цікаві, ласкаво просимо під кат.

щось тут не так...
Для початку визначимося з тим, що ми знаємо. У загальному вигляді Load Average це середня кількість очікують ресурсів ЦПУ процесів за один з трьох проміжків часу. Так само нам відомо, що це значення в нормальному стані перебуває в діапазоні від 0 до 1, і одиниця відповідає 100% завантаженні одноядерної системи без перевантаження. Надалі я буду розглядати систему як одноядерную, оскільки це простіше і показовіша.
Що тут не так?
По-перше, всі ми знаємо, що середнє арифметичне кількох величин дорівнює сумі цих величин, поділеній на їх кількість. З тієї інформації, що у нас є абсолютно незрозуміло це саме кількість. Якщо ми вважаємо очікують процеси протягом всієї хвилини, то середнє значення буде дорівнює кількості процесів за хвилину, поділеному на одиницю. Якщо будемо вважати кожну секунду — то і кількість процесів в кожному підрахунку зменшиться з діапазоном і ділити будемо на 60. Таким чином чим вище частота дискретизації при наборі даних, тим менше середнє значення ми отримаємо.
По друге що значить «очікує ресурсів процес»? Якщо ми запустимо велика кількість швидких процесів разом, то всі вони встануть в чергу, і за логікою на короткий проміжок часу LA повинен зрости до зовсім неприйнятних величин, і при тривалому моніторингу повинні спостерігатися постійні скачки, чого, в нормальній ситуації, немає.
У третіх, одноядерна система при 100% завантаженні повинна давати Load Average рівний 1. Але тут немає ніякої залежності від параметрів цього ядра, хоча кількість процесів може відрізнятися в рази. Дане питання може бути знято або коректним визначенням «очікує ресурсів процесу», або наявністю якийсь нормування на параметри ядра.

Література
Знайти відповіді на поставлені питання виявилося не так вже й складно. Правда, тільки на англійській мові, і не все так відразу стало зрозуміло. Конкретно були знайдені дві статті:
Examining Load Average
UNIX Load Average
А так же невеликий тест для тих, хто і так все розуміє, зазначений у другій статті.
Цікавляться я б радив прочитати обидві статті, хоча в них описані дуже близькі речі. У першій описується в загальному вигляді багато різних цікавих подробиць роботи системи, а другий більш детально розбирається безпосередньо розрахунок LA, наводяться приклади з навантаженням і коментарі фахівців.

Трохи ядерної магії
З даних матеріалів можна дізнатися, що кожному зі своїх процесу дається обмежений проміжок часу на використання CPU, в стандартній архітектурі intel цей проміжок дорівнює 10мс. Це ціла сота частка секунди і в більшості випадків процесу стільки часу не потрібно. Однак, якщо якийсь процес використовував весь відведений йому час, то викликається апаратне переривання і система повертає собі керування процесором. Крім цього кожні 10мс збільшуючи лічильник тактів (jiffies counter). Дані тики вважаються з моменту запуску системи і кожні 500 тиків (раз в 5 секунд) розраховується Load Average.
Код безпосередньо розрахунку знаходиться в ядрі у файлі timer.c (код наведений для версії 2.4, у версії 2.6 все це кілька розосереджено, але логіка не змінилася, далі, сподіваюся, теж суттєвих змін немає, але, чесно кажучи, останні релізи не перевіряв):
646 unsigned long avenrun[3];
647 
648 static inline void calc_load(unsigned long ticks)
649 {
650 unsigned long active_tasks; /* fixed-point */
651 static int count = LOAD_FREQ;
652 
653 count -= ticks;
654 if (count < 0) {
655 count += LOAD_FREQ;
656 active_tasks = count_active_tasks();
657 CALC_LOAD(avenrun[0], EXP_1, active_tasks);
658 CALC_LOAD(avenrun[1], EXP_5, active_tasks);
659 CALC_LOAD(avenrun[2], EXP_15, active_tasks);
660 }
661 }

Як видно, розраховуються по черзі ті самі три значення LA, однак не вказано, що саме вважається, і як саме вважається. Це теж не проблема, код функції count_active_tasks() знаходиться в тому ж файлі, трохи вище:
625 static unsigned long count_active_tasks(void)
626 {
627 struct task_struct *p;
628 unsigned long nr = 0;
629 
630 read_lock(&tasklist_lock);
631 for_each_task(p) {
632 if ((p>state == TASK_RUNNING ||
633 (p->state & TASK_UNINTERRUPTIBLE)))
634 nr += FIXED_1;
635 }
636 read_unlock(&tasklist_lock);
637 return nr;
638 }

А CALC_LOAD лежить в sched.h разом з декількома цікавими константами:
61 #define FSHIFT 11 /* nr of bits of precision */
62 #define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
63 #define LOAD_FREQ (5*HZ) /* 5 sec intervals */
64 #define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
65 #define EXP_5 2014 /* 1/exp(5sec/5min) */
66 #define EXP_15 2037 /* 1/exp(5sec/15min) */
67 
68 #define CALC_LOAD(load,exp,n) \
69 load *= exp; \
70 load += n*(FIXED_1-exp); \
71 load >>= FSHIFT;

З усього перерахованого вище можна сказати, що раз в 5 секунд ядро дивиться, скільки всього процесів знаходиться в стані RUNNING і UNINTERRUPTIBLE (до речі в інших UNIX системах це не так) і для кожного такого процесу збільшує лічильник на FIXED_1, що дорівнює 1<<FSHIFT, або 1<<11, що рівносильно 2^11. Зроблено це для симуляції розрахунку з плаваючою точкою при використанні стандартних змінних int довжиною 32 біта. Зміщуючи після розрахунків результат на 11 біт вправо ми відкинемо зайві порядки. З того ж sched.h:
49 /*
50 * These are the constant used to the fake fixed-point load-average
51 * counting. Some notes:
52 * - 11 bit fractions to expand 22 bits by the multiplies: this gives
53 * a load-average of precision 10 bits integer + 11 bits fractional
54 * - if you want to count load-averages more often, you need more
55 * precision, or rounding will get you. With 2-second counting freq,
56 * the EXP_n values would be 1981, 2034 and 2043 if still using only
57 * 11 bit fractions.
58 */


Трохи ядерного розпаду
Ні, тут не розпадається ядро системи, просто формула CALC_LOAD, за якою вважається Load Average заснована на закон радіоактивного розпаду, або просто експоненціального затухання. Цей закон є не що інше, як рішення диференціального рівняння , тобто кожне нове значення розраховується з попереднього і швидкість зменшення кількості елементів безпосередньо залежить від кількості елементів.
Розв'язком даного диференціального рівняння є експоненціальний закон:

Фактично Load Average не є середнім значенням у звичайному розумінні середнього арифметичного. Це дискретна функція, періодично розраховується з моменту запуску системи. При цьому значення функції є кількість відпрацьовують в системі процесів в умовах експоненціального затухання.
Таку конструкцію ми спостерігаємо, переписавши розрахункову частину CALC_LOAD математичною мовою:

2^11 для нас в даному випадку рівносильно одиниці, ми її зафіксували спочатку і додавали скрізь, кількість нових процесів так само розраховується в цих величинах. А , де T — інтервал виміру (1,5 або 15 хвилин).
Варто зауважити, що при фіксованому часовому інтервалі і фіксованому часу між вимірюваннями значення експоненти цілком можуть бути пораховані заздалегідь і використовуватися як константа, що в коді і робиться. Остання операція — зсув вправо на 11 біт дає нам шукане значення Load Average з відкиданням нижніх порядків.

Висновки
Тепер, розуміючи, як розраховується LA можна спробувати відповісти на запитання, поставлені на початку статті:
1) Середнє значення не є середнім арифметичним, а є середнє значення функції, яка розраховується кожні 5 секунд з моменту старту системи.
2) «Очікують ресурсів CPU» вважаються всі процеси, що знаходяться в стані RUNNING і UNINTERRUPTIBLE. А суттєвих стрибків Load Average при тривалому моніторингу ми не спостерігаємо, оскільки загасаюча експонента грає роль згладжує функції (хоча при розгляді періоду в 1 хвилину їх можна помітити).
3) А ось тут один з найбільш цікавих висновків. Справа в тому, що зазначена вище функція Load Average при будь-яких значеннях n монотонно зростає до цього значення, якщо ж n<L — експоненційно затухає до нього ж. Таким чином LA=1 говорить про те, що в будь-який момент часу CPU зайнятий одним єдиним процесом і черги ніякої немає, що в цілому можна вважати 100% завантаженням, не більше, не менше. У той же час LA<1 говорить про те, що CPU простоює, а якщо у вас є безліч процесів, які стукають на неробочий nfs то можна побачити і
ось таке


Однак крім відповідей на що були спочатку питання розбір коду ставить і нові. Наприклад, застосовується загасаюча експонента до скорочення числа очікують процесів? Якщо ми розглядаємо радіоактивний розпад, то його швидкість обмежена лише кількістю ядер, в нашому ж випадку, при великій кількості процесів все упреться в пропускну здатність CPU. Так само, якщо порівняти отриману формулу з експоненціальним законом, стає видно, що , де T — тривалість інтервалу набору даних (1,5 або 15 хвилин). Таким чином розробники ядра вважають, що швидкість зменшення Load Average обернено пропорційна тривалості вимірювань, що кілька неочевидно, принаймні для мене. Ну і не складно змоделювати ситуації, коли величезні значення LA не будуть реально відображати завантаження системи, або навпаки.
В остаточному підсумку складається враження, що для розрахунку Load Average була обрана сглаживающая функція, максимально швидко зменшує своє значення, що загалом логічно для отримання звичайно числа, але не відображає реально процесу, що відбувається. І якщо хто-небудь мені пояснить, чому саме експонента і чому саме в такому вигляді, буду дуже вдячний.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.