1. 背景
Linux社区的负载评估算法默认使用pelt算法,研究这个算法的过程中发现有个get_pelt_divider函数初看有些晦涩,其实实现如下:
40 #define PELT_MIN_DIVIDER (LOAD_AVG_MAX - 1024)
41
42 static inline u32 get_pelt_divider(struct sched_avg *avg)
43 {
44 return PELT_MIN_DIVIDER + avg->period_contrib;
45 }
46
这个函数的目标:计算一个任务连续运行无限个周期之后,最大的负载贡献的机极限值,根据内核的定义PELT_MIN_DIVIDER是:LOAD_AVG_MAX - 1024,而LOAD_AVG_MAX 47742,如果想了解get_pelt_divider函数的计算原理有两个问题需要搞清楚:
- LOAD_AVG_MAX 这个数字的含义,以及计算公式
- 为什么get_pelt_divider函数要把avg->period_contrib计算在内
2.LOAD_AVG_MAX是怎么来的
我们知道PELT一个小周期是1ms(1024us),如果一个任务持续运行无限个1ms周期其负载贡献值是多少呢?公式如下:
1024 *y^0 + 1024*y^1 + 1024*y^2 + ... + 1024 * y^(n-1) = 1024 (1-y^n)/(1-y)
由于n趋近无穷大,那么公式可以简化为 1024/ (1-y),由于y^32 = 0.5,所以这个等比数据的值就是47742,即LOAD_AVG_MAX
3.get_pelt_divider函数原理揭秘
上面我们明白了LOAD_AVG_MAX,那么get_pelt_divider直接返回LOAD_AVG_MAX不久好了吗,内核为什么还要考虑period_contrib呢?这就考虑了内核计算的精准,LOAD_AVG_MAX假设的是某个任务运行无限个“完整的” 1ms,但是实际上任务往往不会这么精准运行时间是1ms的整数倍周期,示意图如下:
所以get_pelt_divider实现的时候将B段时间去掉了