2.7.3 负载追踪 - 计算负载

虽然负载的计算公式是一个无穷级数之和,但由于我们对历史负载并不感兴趣,因此计算起来还是很方便的,假设系统每个周期都会计算一次负载,那么当前负载的计算方式为 L = L0 + L1*y, 其中L1是上一个周期的负载,这样从第一个周期开始,每个周期在计算负载时都已经完成了对前面所有周期的负载累计。

然而问题是计算负载的时间点并没有这么精确,两次的时间差可能跨越了多个周期,如下图所示:

其中t1是上次系统计算负载的时间点,假设当时的负载为u, now是当前时间,每个周期为1024us, 那么根据Linux负载的计算公式,当前系统的负载为: u' = u*y^p + d1*y^p + 1024 * (y^(p-1) + y^(p-2) + ... + y) + d3, 我们将该公式分为两部分来分析:

  1. L2 = u*y^p ,其中 u+d1 为一常数,系统需要找到高效的方式计算 y^p. 为了避免浮点数运算,系统将该指数运算等价于 y^p=y^p *2^32 >> 32, 即先乘以一个 232, 再将结果右移32位。为了提升效率,系统将 y^p*2^32 的值提前计算好了存放在数组 runnable_avg_yN_inv 中,其内容如下:

    该数组只有32个元素,因为 y^32=0.5, 所以当 p>32 时,我们可以将 y^p*2^32 转换为 0.5*y^(p-32)*2^32 进行计算。

    系统实现该部分计算的函数是 decay_load:

  2. L1 = d1*y^p + 1024 * (y^(p-1) + y^(p-2) + ... + y) + d3 这里我们详细讨论 1024 * (y^(p-1) + y^(p-2) + ... + y) 这部分数列的计算方式,该部分的推导如下图所示:

    等比数列的求和公式为: S = a(1-q^n)/(1-y), 因此级数 yp 的和式为:(1-yn)/(1-y), 当n趋近于无穷时,结果为 1/(1-y), 我们知道 y32 = 0.5, 因此系统可以提前将 1024/(1-y) 的值计算出来,该值保存在宏 LOAD_AVG_MAX 中:

    另外 y^p * 1024/(1-y) 就是将 LOAD_AVG_MAX 衰减p次,可以调用前文介绍过的函数 decay_load 来完成计算。因此整个数列的和为: LOAD_AVG_MAX - decay_load(LOAD_AVG_MAX, periods) - 1024.

    整个L1 的计算函数是 __accumulate_pelt_segments:

    其中c1 = d1*yp, c3 = d3, 而参数 periods 就是上图中的 p=delta/1024.

理解了上面的逻辑之后,我们再来看累计负载的函数 accumulate_sum:

该函数就是前面所讨论过的整个负载算法的实现。

Last updated

Was this helpful?