Bash

重現負載平均計算

  • June 24, 2020

我正在研究洛杉磯的計算。那麼,我發現了什麼:

  1. 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(不可中斷)程序的數量
  1. 這是做這些事情的核心程式碼(以及一些帶有浮點數的魔法):
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;
  1. 我編寫了嘗試做同樣事情的天真的 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()和我的腳本使用 simple ps.

我能以某種方式從 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

引用自:https://unix.stackexchange.com/questions/591415