Bash
重現負載平均計算
我正在研究洛杉磯的計算。那麼,我發現了什麼:
- LA 每五秒計算一次指數移動平均值:
LA(t) = LA(t-1) + EXP_R * (n(t) - LA(t-1))
在哪裡
- LA(t-1) - 是上一次迭代中記錄的 LA
- EXP_R - 是 1、5 和 15 分鐘的三個預定義常量
- n(t) - 系統中
R
(正在執行)或D
(不可中斷)程序的數量
- 這是做這些事情的核心程式碼(以及一些帶有浮點數的魔法):
unsigned long avenrun[3]; static inline void calc_load(unsigned long ticks) { unsigned long active_tasks; /* fixed-point */ static int count = LOAD_FREQ; count -= ticks; if (count < 0) { count += LOAD_FREQ; active_tasks = count_active_tasks(); CALC_LOAD(avenrun[0], EXP_1, active_tasks); CALC_LOAD(avenrun[1], EXP_5, active_tasks); CALC_LOAD(avenrun[2], EXP_15, active_tasks); } } static unsigned long count_active_tasks(void) { struct task_struct *p; unsigned long nr = 0; read_lock(&tasklist_lock); for_each_task(p) { if ((p->state == TASK_RUNNING || (p->state & TASK_UNINTERRUPTIBLE))) nr += FIXED_1; } read_unlock(&tasklist_lock); return nr; } #define FSHIFT 11 /* nr of bits of precision */ #define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */ #define LOAD_FREQ (5*HZ) /* 5 sec intervals */ #define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */ #define EXP_5 2014 /* 1/exp(5sec/5min) */ #define EXP_15 2037 /* 1/exp(5sec/15min) */ #define CALC_LOAD(load,exp,n) \ load *= exp; \ load += n*(FIXED_1-exp); \ load >>= FSHIFT;
- 我編寫了嘗試做同樣事情的天真的 bash 腳本:
#!/usr/bin/env bash set -euo pipefail LA_1=0 LA_5=0 LA_15=0 EXP_1=0.0800 EXP_5=0.0165 EXP_15=0.0055 count() { echo $(ps -eostat | grep -E "R|D" | wc -l) } echo "1 min 5 min 15 min" while true; do n=$(($(count) - 1)) # -1 to eliminate `ps` from the result LA_1=$(bc -l <<<"$LA_1 + $EXP_1 * ($n - $LA_1)") LA_5=$(bc -l <<<"$LA_5 + $EXP_5 * ($n - $LA_5)") LA_15=$(bc -l <<<"$LA_15 + $EXP_15 * ($n - $LA_15)") echo -ne "$LA_1 $LA_5 $LA_15\r" sleep 5 done
但我的劇本的結果與實際的洛杉磯相去甚遠。我認為主要區別在於活動程序的計數,其中核心呼叫
count_active_tasks()
和我的腳本使用 simpleps
.我能以某種方式從 bash 中獲得更精確的活動任務數量嗎?或者也許我在其他地方做錯了?
UPD:我正在執行我的腳本一段時間,結果如下:
1 min 5 min 15 min .42342580723140551985 .53553677285166903835 .35305247755440928285
雖然實際的洛杉磯是:
load average: 0.80, 1.63, 1.54
核心原始碼取自這篇解釋 LA 的文章:https ://wiki.nix-pro.com/view/Load_average_explained
UPD:我的腳本中 EXP_R 的定義與核心原始碼中的定義不同:在我的腳本中,它實際上是 1 - exp_kernel(其中 exp_kernel - 是核心原始碼中的定義)。它不影響最終結果,因為最終因素相同
感謝@muru,他在我使用的公式中發現了錯誤。這是正確的,結果非常準確:
#!/usr/bin/env bash set -euo pipefail LA_1=0 LA_5=0 LA_15=0 EXP_1=0.9200 EXP_5=0.9835 EXP_15=0.9945 count() { echo $(ps -eostat | grep -E "R|D" | wc -l) } echo "1 min 5 min 15 min" while true; do n=$(($(count) - 1)) LA_1=$(bc -l <<<"$LA_1 * $EXP_1 + $n * (1 - $EXP_1)") LA_5=$(bc -l <<<"$LA_5 * $EXP_5 + $n * (1 - $EXP_5)") LA_15=$(bc -l <<<"$LA_15 * $EXP_15 + $n * (1 - $EXP_15)") echo -ne "$LA_1 $LA_5 $LA_15\r" sleep 5 done