tang-hi

Don't Panic

有趣的知识 -- CPU利用率,延迟,吞吐量之间的关系

Posted at # Interesting

本文从排队论的角度出发分析CPU利用率,延迟和吞吐量之间的关系,这篇文章大量参考喵叔的文章, 油管视频, 本质上只是简单的复述,作为我自己学习的整理。

CPU利用率和延迟之间的关系

做在线服务的时候,我们经常会给CPU利用率设置一个阈值,如果超过了这个阈值,我们会对服务扩容。这个阈值到底应该如何选择,为什么当CPU利用率到达了50%(意味着它有一半时间是空闲的),我们就需要对其进行扩容? 要回答这些问题就意味着,我们不能仅仅对CPU和延迟有一个感性的认识,而应该有一个定量的描述。

1. Little’s Law

Little’s Law 是排队论中的一个重要定律,由 John D. C. Little 在 1961 年提出。该定律指出,在一个系统中,平均的顾客数等于平均的到达率乘以平均的服务时间。 换算到计算机中即系统中的平均请求数等于单位时间内的请求数量乘以平均的处理时间,其公式可以表示为

L=λWL = \lambda W

其中:

LL : 系统中的平均请求数,即当前正在处理以及待处理的请求

λ\lambda : 单位时间内到达的请求数,即QPS

WW : 单个请求的平均处理时间, 即延迟

2. 模型构建

在得知上述定理后,我们可以对我们的服务进行一个简单的模型构建M/M/1模型

  1. 我们只有一个服务器
  2. 服务器一次只处理一个请求
  3. 请求的到来服从泊松分布
  4. 请求的处理时间服从泊松分布

因为整个服务是动态的,每时每刻待处理的请求数量都是变化的,但是它们都服从马尔科夫链,其中每个状态的变化都是一个泊松过程。

因此如果当前状态为 ii , 那么它的上一个状态为 jj ( i±1i \pm 1 ), 由于整个系统处于平稳的状态,所以离开状态的速率等于进入状态的速率, 即

λP(X=i)=μP(X=j)\lambda * P(X=i) = \mu * P(X=j)

其中:

P(X=i)P(X=i) 代表状态ii的概率,即请求数量为ii的概率

P(X=j)P(X=j) 代表状态jj的概率,即请求数量为jj的概率

λ\lambda 代表QPS

μ\mu 代表单位时间内处理请求的数量

根据上面的式子,我们可以得到如下的递推式

λP(X=0)=μP(X=1)λP(X=1)=μP(X=2)λP(X=2)=μP(X=3)..\lambda * P(X=0) = \mu * P(X=1)\newline \lambda * P(X=1) = \mu * P(X=2)\newline \lambda * P(X=2) = \mu * P(X=3)\newline .\newline .\newline

我们可以从上述公式推导出

P(X=i)=(λμ)nP(X=0)P(X=i) = (\frac{\lambda}{\mu})^n * P(X=0)

又由于所有的概率之和为1

i=0P(X=i)=1(λμ)0P(X=0)+(λμ)1P(X=0)...(λμ)nP(X=0)=1P(X=0)=(μλ)μ\sum_{i=0}^{\infty}P(X=i) = 1\newline (\frac{\lambda}{\mu})^0 * P(X=0) + (\frac{\lambda}{\mu})^1 * P(X=0) ... (\frac{\lambda}{\mu})^n * P(X=0) = 1\newline P(X=0) = \frac{(\mu - \lambda)}{\mu}

同样的我们如果按照这个模型来计算平均待处理的请求数,可以得到

i=0P(X=i)i=P(X=0)λμ(1(1λμ)2)\sum_{i=0}^{\infty}P(X=i) * i\newline = P(X=0) * \frac{\lambda}{\mu} * (\frac{1}{(1-\frac{\lambda}{\mu})^2})

将P(X=0)代入可以得到

L=λ2μ(μλ)L = \frac{\lambda^2}{\mu * (\mu - \lambda)}

再通过Little’s Law的公式我们可以得到

W=λμ(μλ)W = \frac{\lambda}{\mu * (\mu - \lambda)}

整个推导过程较长,你可以只记住最后一个公式,最后我们得到延迟QPS以及单位时间处理请求量之间的关系。

3.CPU利用率的表示

我们可以将CPU的利用率看作一段时间内的请求数量除以同一段时间内CPU最大可处理的请求数量,那么我们有以下公式

ρ=λTμT=λμ\rho = \frac{\lambda * T}{\mu * T} = \frac{\lambda}{\mu}

将其代入第二节我们得到的公式可以得到延迟CPU利用率之间的关系

W=ρμ(1ρ)W = \frac{\rho}{\mu*(1-\rho)}

那么这个图具体长什么样呢?我们可以通过Walfram查看

从这幅图中我们可以发现随着CPU利用率的提升,延迟会快速的增长,我们可以通过下面的数据更具体的感受这一关系

CPU usagelatencylatency increase
0.20.0025
0.30.004268%
0.40.00642%
0.50.0166%
0.60.01550%
0.70.02353%
0.80.04073%
0.90.090125%
0.990.991000%

可以看到每次CPU利用率提升10%,延迟就会增加50%以上。如果CPU利用率到达80%,延迟的增长率甚至能到125%. 所以,我们可以得出以下结论

  1. 为什么有时候CPU利用率到达了50%就需要扩容? 因为延迟的增长和CPU的利用率并不是线性增长.
  2. 如果CPU利用率到达了80%,就需要高度重视服务的负载了.
  3. 如果你想要低延迟,你需要保持低CPU利用率。在代码不变的情况下,高CPU利用率意味着高延迟。(想到了之前公司领导提出的高利用率,低延迟的降本增效项目)

吞吐量和延迟之间的关系

吞吐量和延迟一直是系统调优中永恒不变的话题,那么他们的到关系到底是怎么样的,我们能否像之前一样通过某种公式来描述它?

1. 模型构建

首先我们假定以下条件

  1. 我们只有一个服务器
  2. 服务器一次只处理一个请求
  3. 请求的到来服从泊松分布
  4. 请求的处理时间服从泊松分布
  5. 每个请求的处理时间保持一样

根据以上的假设,我们可以用以下的图片来表明该模型,横轴表明时间,纵轴表明待处理的工作量。

我们可以用下面的图来表明更一般的情况,即有可能一个请求还没处理完,下一个请求就来了。

现在我们可以尝试计算这个图形的面积,我们有两种方式计算这个图像的面积

第一种方式

Area=T(average  height)=T(average  wait  time)=TWArea = T * (average ~~ height) \newline = T * (average ~~ wait ~~ time) \newline = T * W\newline

其中:

TT 为时间的长度

WW 为请求的平均等待时间

第二种方式

Area=(area of triangles)+(area of parallelograms)=(number of request)(area of single triangle+area of single parallelogram)=Tλ((S22)+SW)Area = (area ~ of ~ triangles) + (area ~ of ~ parallelograms)\newline = (number ~ of ~ request) * (area ~ of ~ single ~ triangle + area ~ of ~ single ~ parallelogram) \newline = T*\lambda * ( (\frac{S^2}{2}) + S * W)

其中:

TT 为时间的长度

λ\lambda 为单位时间内可以处理的请求数,即吞吐量

SS 为每个请求的处理时间

WW 为请求的平均等待时间

我们将上面两个公式进行求解

TW=Tλ((S22)+SW) T * W = T*\lambda * ( (\frac{S^2}{2}) + S * W)

可以得到

W=λS22(1Sλ)W = \frac{\lambda*S^2}{2 * (1 - S\lambda)}

如果我们固定S(每个请求的处理时间), 我们可以获得以下W与 λ\lambda 的函数图像,即延迟和吞吐量的关系。

从该公式我们可以得到以下结论

  1. 延迟与高吞吐成正比,也就是说低延迟和高吞吐是不可能同时存在的。
  2. 我们也获得了一个符合直觉的情况,如果要高吞吐你需要高CPU利用率,如果你需要低延迟你需要低CPU利用率
  3. 如果你可以将处理请求的时间缩短一半,你可以在吞吐量增长一倍的情况下依旧保持之前一般的延迟。

结论

  1. 为什么有时候CPU利用率到达了50%就需要扩容? 因为延迟的增长和CPU的利用率并不是线性增长.
  2. 如果CPU利用率到达了80%,就需要高度重视服务的负载了.
  3. 如果你想要低延迟,你需要保持低CPU利用率。在代码不变的情况下,高CPU利用率意味着高延迟
  4. 延迟与高吞吐成正比,也就是说低延迟和高吞吐是不可能同时存在的。
  5. 我们也获得了一个符合直觉的情况,如果要高吞吐你需要高CPU利用率,如果你需要低延迟你需要低CPU利用率
  6. 如果你可以将处理请求的时间缩短一半,你可以在吞吐量增长一倍的情况下依旧保持之前一般的延迟。