負載均衡的計算方法和含義
我們在執行top 或者uptime時,會計算當前系統的負載平衡。
當前系統的負載平衡是看我們cpu對於當前執行任務的一個執行情況。同時也判斷我們系統的任務
對於cpu的需求情況。
這句話是對於負載平衡的一個很好的總結:
The CPU percentage shows us how much the cars are using the freeway, but the load averages show us the whole picture, including pent-up demand.
以下是我收集的負載平和的計算方法
負載的檢視
在Linux系統中,使用uptime、w和top命令都可以檢視負載,或者直接通過cat /proc/loadavg檢視。
比如說uptime命令:
20:56:33 up 22 min, 3 users, load average: 0.39, 0.25, 0.10
可以看到有三個值,這三個值分別表示:
前1分鐘系統的平均負載
前5分鐘系統的平均負載
前15分鐘系統的平均負載
下面就來看看負載的含義。
負載的含義
負載的含義是執行佇列的平均長度。
具體而言,對於Linux系統,其統計了最近1分鐘、最近5分鐘、最近15分鐘處於running或者uninterruptible sleep狀態的程序數。
文末參考資料裡給出了一個車道的例子進行類比,這兒就不贅述了。舉幾個例子:
負載為0.5,表示CPU有平均一半的時間是空閒的 負載為1,這時就表示CPU被充分利用了 負載為2,則表示負載過大,有一半的任務在等待CPU執行
一般認為負載為0.7是警戒線。
負載與多處理器
對於多處理器,相當於多車道。比如對於雙處理器,那麼負載為2才算滿。 可以簡單地將負載除以CPU核心數換算成單處理的情況。
負載與CPU使用率
文末參考資料中提到:
The CPU percentage shows us how much the cars are using the freeway, but the load averages show us the whole picture, including pent-up demand.
相比於CPU使用率,負載可能展示更多的資訊,即CPU的需求量。
負載和多執行緒
Wikipedia提到在不同的Unix系統中多執行緒負載的計算方式有很多種,比如執行緒與核實體間不同的模型就有所不同。而在Linux系統中每個執行緒應該都是獨立計算的。
我並不瞭解統計學等知識,以下只是個人一些粗淺的理解。
負載計算原理是統計學的EWMA(Exponentially Damped/Weighted Moving Average),利用指數衰減函式遞推出當前序列平均值。
Wikipedia裡提到:
Hence, the 1-minute load average consists of 63% (more precisely: 1 - 1/e) of the load from the last minute and 37% (1/e) of the average load since start up, excluding the last minute. For the 5- and 15-minute load averages, the same 63%/37% ratio is computed over 5 minutes and 15 minutes respectively. Therefore, it's not technically accurate that the 1-minute load average only includes the last 60 seconds of activity (it actually includes 37% of the activity from the past), but it is correct to state that it includes mostly the last minute.
即統計計算出一段時間的負載,其本質是計算均值。比如說:
要計算——最近x分鐘負載:$la_{-x}$
假設已知——自系統啟動到x分鐘前的負載為:$la_{+x}$
假設可以得到——近x分鐘的執行的任務數為:$n_x$
那麼 l a − x = l a + x t i m e s ( e − 1 ) + n x t i m e s ( 1 − e − 1 ) la_{-x} = la_{+x} times (e^{-1}) + n_x times (1-e^{-1}) la−x=la+xtimes(e−1)+nxtimes(1−e−1) 即 l a − x = l a + x t i m e s 0.37 + n x t i m e s 0.63 la_{-x} = la_{+x} times 0.37 + n_x times 0.63 la−x=la+xtimes0.37+nxtimes0.63
可以看出 x x x分鐘前的負載權重是比較小的,隨著時間的推移前面的資訊影響會越來越小,直到趨近於0,最近時間的資訊影響的比重比較大。
而在Linux系統中,是每間隔 5 H Z 5HZ 5HZ或者 5 秒 5秒 5秒統計一次執行的任務數,然後計算均值。那麼隨著時間推移,可以得出這麼一個序列:
n 1 , n 2 , n 3 , . . . , n t − 1 , n t n_1, n_2, n_3, ..., n_{t-1}, n_t n1,n2,n3,...,nt−1,nt
當前時刻 t t t的Moving Average—— m v t mv_t mvt同樣根據 m v t − 1 mv_{t-1} mvt−1計算出:
m v t = m v t − 1 t i m e s ( e − l a m b d a ) + n t t i m e s ( 1 − e − l a m b d a ) mv_t = mv_{t-1} times (e^{-lambda}) + n_t times (1-e^{-lambda}) mvt=mvt−1times(e−lambda)+nttimes(1−e−lambda)
對於要統計最近1分鐘(60秒)的均值,由於時間間隔為5秒,那麼 l a m b d a lambda lambda的取值應為 f r a c 560 frac 5{60} frac560;同理,最近5分鐘(300秒)的均值, l a m b d a lambda lambda的取值應為 f r a c 5300 frac 5{300} frac5300;最近15分鐘(900秒)的均值, l a m b d a lambda lambda的取值應為 f r a c 5900 frac 5{900} frac5900。
下面來看看Linux中如何實現負載的計算。
負載計算的實現
Linux核心中維護了一個Jiffies變數,記錄自開機一開經歷的Tick數,每次更新Jiffies時系統會呼叫timer.c中的do_timer();
unsigned long avenrun[3];
static inline void (unsigned long ticks)
{
unsigned long active_tasks;
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);
}
}
在sched.h中可以看到上面calc_load()裡一些變數、巨集的定義:
extern unsigned long avenrun[]; /* Load averages */
#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;
可以看到,每次Jiffies+1時呼叫calc_load(),並不會馬上統計計算負載資訊,而是等到一個LOAD_FREQ的週期結束,隨後又開始新的LOAD_FREQ週期;
通過呼叫count_active_tasks()可以獲得處於running或者uninterruptible sleep狀態的程序數;
最後執行三次的CALC_LOAD,分別更新統計的1分鐘、5分鐘和15分鐘的負載資訊。
值得一提的就是CALC_LOAD的實現,其本質是將上面的Moving Average計算的式子乘以 2 11 2^{11} 211,最後再除以 2 11 2^{11} 211,採用位運算並且避免了浮點數運算,提高了計算效率。