1. 首页
  2. > 公司注册 >

加权移动平均系数法(加权移动平均法设置不同的加权系数)

简介:双十一压测过程中,常见的问题之一就是load 飙高,通常这个时候业务上都有受影响,比如服务rt飙高,比如机器无法登录,比如机器上执行命令hang住等等。本文就来说说,什么是load,load是怎么计算的,什么情况下load 会飙高,load飙高是不是必然业务受影响。



双十一压测过程中,常见的问题之一就是load 飙高,通常这个时候业务上都有受影响,比如服务rt飙高,比如机器无法登录,比如机器上执行命令hang住等等。本文就来说说,什么是load,load是怎么计算的,什么情况下load 会飙高,load飙高是不是必然业务受影响。


一 什么是load

我们平时所讲的load,其全称是Linux system load averages ,即linux系统负载平均值。注意两个关键词:一个是“负载”,它衡量的是task(linux 内核中用于描述一个进程或者线程)对系统的需求(CPU、内存、IO等等),第二个关键词是“平均”,它计算的是一段时间内的平均值,分别为 1、5 和 15 分钟值。system load average由内核负载计算并记录在/proc/loadavg 文件中, 用户态的工具(比如uptime,top等等)读的都是这个文件。


我们一般认为:


  • 如果load接近0,意味着系统处于空闲状态
  • 如果 1min 平均值高于 5min 或 15min 平均值,则负载正在增加
  • 如果 1min 平均值低于 5min 或 15min 平均值,则负载正在减少
  • 如果它们高于系统 cpu 的数量,那么系统很可能遇到了性能问题(视情况而定)

二 如何计算load

1 核心算法

坦白了不装了,核心算法其实就是指数加权移动平均法(Exponential Weighted Moving Average,EMWA),简单表示就是:


a1 = a0 factor a (1 - factor),其中a0是上一时刻的值,a1是当前时刻的值,factor是一个系数,取值范围是[0,1],a是当前时刻的某个指标采样值。


为什么要采用指数移动加权平均法?我个人理解


1、指数移动加权平均法,是指各数值的加权系数随时间呈指数式递减,越靠近当前时刻的数值加权系数就越大,更能反映近期变化的趋势;


2、计算时不需要保存过去所有的数值,这对内核非常重要。


我们来看看,内核是怎么计算load average的,以下简称load。


上面的指数移动平均公式,a1 = a0 e a (1 - e),具体到linux load的计算,a0是上一时刻的load,a1是当前时刻的load,e是一个常量系数,a 是当前时刻的active的进程/线程数量。


如上一节所述,linux 内核计算了三个load 值,分别是1分钟/5分钟/15分钟 load 。计算这三个load 值时,使用了三个不同的常量系数e,定义如下:


#define EXP_1 1884 /* 1/exp(5sec/1min) */ #define EXP_5 2014 /* 1/exp(5sec/5min) */ #define EXP_15 2037 /* 1/exp(5sec/15min) */

这三个系数是怎么来的呢?公式如下:


  • 1884 = 2048/(power(e,(5/(601)))) / e = 2.71828 */
  • 2014 = 2048/(power(e,(5/(60*5))))
  • 2037 = 2048/(power(e,(5/(60*15))))

其中e=2.71828,其实就是自然常数e,也叫欧拉数(Euler number)。


那为什么是这么个公式呢?其中,5是指每五秒采样一次,60是指每分钟60秒,1、5、15则分别是1分钟、5分钟和15分钟。至于为什么是2048和自然常数e,这里涉及到定点计算以及其他一些数学知识,不是我们研究的重点,暂时不展开讨论。


我们看看内核中实际代码:


/* * a1 = a0 * e a * (1 - e) */ static inline unsigned long calc_load(unsigned long load, unsigned long exp, unsigned long active) { unsigned long newload; // FIXED_1 = 2048 newload = load * exp active * (FIXED_1 - exp); if (active >= load) newload = FIXED_1-1; return newload / FIXED_1; }

就是一个很直观的实现。上面代码中,第一个参数就是上一时刻的load, 第二个参数就是常量系数,第三个参数是active的进程/线程数量(包括runnable 和 uninterruptible)。


2 计算流程

load的计算分为两个步骤:


1、周期性地更新每个CPU上的rq里的active tasks,包括runnable状态和uninterruptible状态的task,累加到一个全局变量calc_load_tasks。


2、周期性地计算 load,load的计算主要就是基于上述calc_load_tasks 变量。


第一个步骤,每个cpu都必须更新calc_load_tasks,但是第二个步骤只由一个cpu来完成,这个cpu叫tick_do_timer_cpu,由它执行do_timer() -> calc_global_load()计算系统负载。


整体流程如下图所示,在每个tick到来时(时钟中断),执行以下逻辑:



上图中,棕色的calc_global_load_tick函数就是完成第一个步骤的,绿色的calc_global_load 是完成第二个步骤,蓝色的calc_load 就是上一节中描述的核心算法。


这里需要说明的是,calc_global_load 把计算出来的load 值放在一个全局的变量avenrun中,它的定义是unsigned long avenrun[3],size 是3,用于存放1/5/15分钟的load。 当查看/proc/loadavg的时候,就是从这个avenrun数组中获取数据。


三 load高常见原因

从上述load的计算原理可以看出,导致load 飙高的原因,说简单也简单,无非就是runnable 或者 uninterruptible 的task 增多了。但是说复杂也复杂,因为导致task进入uninterruptible状态的路径非常多(粗略统计,可能有400-500条路径)。个人觉得,有些地方有点滥用这个状态了。


本人基于多年的linux 内核开发和疑难问题排查经验,总结了一些经验,以飨读者。


一文说清linux system load


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至123456@qq.com 举报,一经查实,本站将立刻删除。

联系我们

工作日:9:30-18:30,节假日休息