TCP拥塞控制和PID控制器

我一直都很喜欢任何的负反馈系统,因为它们无以伦比的稳定性,比如说喝酒。你完全不用担心喝酒能把人喝死,因为当你的机体不能再承受更多酒精时,你就会醉去不省人事,也就自然没法继续喝了。

周末了,写点儿与此相关的。

先看下什么是PID控制:
https://zh.wikipedia.org/wiki/PID控制器

PID控制是简单的,因此很受欢迎。PID为什么简单,因为它直观啊。看下它的一般表达式:

u ( t ) = K p e ( t ) + K i ∫ 0 r e ( r ) d r + K d d e ( t ) d t u(t)=K_pe(t)+K_i\displaystyle\int_0^re(r)dr+K_d\dfrac{de(t)}{dt} u(t)=Kpe(t)+Ki0re(r)dr+Kddtde(t)

其中 e ( t ) e(t) e(t)为误差,u(t)为输出。直观地可以看出组成输出的3个部分:

K p e ( t ) K_pe(t) Kpe(t):线性部分,缺多少补多少,很容易理解。

K i ∫ 0 r e ( r ) d r K_i\displaystyle\int_0^re(r)dr Ki0re(r)dr:积分部分,用历史误差来纠损,补偿线性部分的损失。

解释一下线性部分的损失是什么。虽然却多少补多少,但你的补偿并非全部用于增益,可能会有额外的损失,比如说往一只漏皮鞋里加水。

K d d e ( t ) d t K_d\dfrac{de(t)}{dt} Kddtde(t):微分部分,用来刹车和纠偏,它体现了对当前趋势的反作用力。

解释一下刹车和纠偏。从表达式可以看出,越是从正方向接近稳态,微分部分就是负数,且绝对值越来越大,一旦超过了稳态,绝对值仍然增加,相当于将过度的积累补偿拉回稳态。

线性部分和积分部分可以让输出收敛到稳态,而微分部分可以让这个稳态更稳定,一旦偏了,微分部分会迅速将输出拉回来。

更详细的就不多说了,知乎上有很多写得很好的帖子:
https://www.zhihu.com/question/23088613/answer/865540435

https://zhuanlan.zhihu.com/p/39573490

理解和运用PID公式并不难,难的是如何调节三个参数,使系统达到最优。

涉及到三个以及三个以上的参数调节,就不是手工调节能搞定的了,其中非线性因素非常复杂,一般而言需要引入一个目标函数,然后运用机器学习的方法让这个目标函数输出最佳得分,但这是另一个话题,本文不谈(因为我也不懂,只是稍微懂一点点)。

现在看一个实际问题,给定一个恒定的带宽值 C C C,如何让一条TCP流的有效吞吐率收敛到该值,这个问题非常适合用PID来解。

b ( t ) b(t) b(t)为当然测量速率,则PID式子如下:

b ( t ) = K p ( b ( t − 1 ) − C ) + ∑ K i b ( r ) + K d ( b ( t − 1 ) − b ( t − 2 ) ) b(t)=K_p(b(t-1)-C)+\sum K_ib(r)+K_d(b(t-1)-b(t-2)) b(t)=Kp(b(t1)C)+Kib(r)+Kd(b(t1)b(t2))

经常有朋友问我,一个40Gbps的网卡,单条TCP流如何跑满带宽。CUBIC是不行的,因为它对丢包都特别敏感,我觉得实现一个体现上述公式的算法,应该是最佳的方法。

但为什么在现实中很难看到PID在拥塞控制中的应用呢?

原因有两点:

  • 对于端到端的TCP流而言,带宽并不是已知的,相反,探测带宽的值却是拥塞控制要解决的问题之一。
  • 带宽在所有TCP流之间共享,给定时刻TCP流的数量未知,如何保持公平是拥塞控制的另一个问题。

在多流之间保持公平似乎更重要,也就是保持每条流看到视图的唯一性。虽然BBR算法可以看作是只有线性部分的PID控制实例,但这仅占BBR算法的30%左右,BBR算法的ProbeRTT状态带来的公平性在PID控制中无法体现。

单纯的PID控制是无法做拥塞控制算法核心的,必须加入MD(multiplicative-decrease)机制。

我们来看下BBR算法。

BBR算法并非理论及想象的好,即使在稳态也能难免出现排队,很多端到端不可控的因素导致BBR并非一个简单的状态机可以hold住的。

因此,在BBR算法线性部分的基础上增加积分和微分部分来监控和控制排队,将会有收益。有一个BBRx算法扩展了BBR,就是这个意思,paper链接如下:
BBRx

这个算法其实是将中间节点的AQM控制移植到了端上。

在我没有接触PID控制之前,我对AQM的细节并不清楚,我很难讲出到底满足什么条件才会判定拥塞发生,进而采取丢包动作,该动作被端探测到,进而拥塞控制起作用。当我清楚了这背后的机制后,就可以解释很多事情了。

我们看下一个简单的PI控制AQM的式子:

p = p + α ( δ γ C − b ) + β ( q 0 − q ) p=p+\alpha(\delta\gamma C-b)+\beta(q_0-q) p=p+α(δγCb)+β(q0q)

其中 α ( δ γ C − b ) \alpha(\delta\gamma C-b) α(δγCb)是积分项, β ( q 0 − q ) \beta(q_0-q) β(q0q)是线性部分,它们都加了负号,因为拥塞是一种负压。

为什么这个AQM没有使用微分项呢?因为AQM仅仅是在拥塞发生时丢包以指示拥塞,而并不控制拥塞,控制拥塞是端到端协议感知到这个拥塞指示后要做的事情,比如TCP的拥塞控制。

如果将AQM逻辑直接在端上做,显然是更加直接的拥塞控制。然而在端上,并没有办法统计其它流的瞬时流量以及累计接收量,因此这个PID控制无法像AQM那样在全局意义上起作用,对于拥塞控制而言,全局意义非常重要。因此只能采用变通的方法,上述paper讲的方法依然简陋,更有意义的测量值包括:

  • 监测排队时间以及其变化。
  • 监测RTT以及其变化。
  • 监测ACK到达的间隔以及其变化。

而不仅仅是当前实际带宽。

和Reno/CUBIC,BBR以及上述BBRx不同,这次使用了完全的PID控制去控制所有这些监测值:

  • 控制排队时间在一定范围内。
  • 控制RTT在一定范围内。
  • 控制ACK到达间隔在一定范围内。(有点难,但还是可以试一下)

我试着写一个来着,结果被Linux内核里无法使用浮点运算的问题所阻碍,很难简单计算一个pacing rate,算了。


浙江温州皮鞋湿,下雨进水不会胖。

Guess you like

Origin blog.csdn.net/dog250/article/details/119102471