浙江温州皮鞋湿!

在前面几篇文章中,我采用纯数学的方式推导了TCP BBR Startup gain的由来,本文将通过一个BBR动力学模型对Startup
gain的值做出直观地解释。

BBR动力学模型

BBR的动力学模型很简单,即:

send_rate(t)=gain×Delivery_rate(t−1)send_rate(t)=gain×Delivery_rate(t−1)
其中t以计时周期为单位,可以是连续到来的ack,也可以是RTT其中t以计时周期为单位,可以是连续到来的ack,也可以是RTT

首先我们先看一下BBR是如何估算带宽的,知道了如何估算带宽,那么用估算出来的带宽bwbw乘以gain,那么就是当前所需的发送速率,即PacingRatePac
ingRate了。

带宽估算的标准详见:
Delivery Rate Estimation (draft-cheng-iccrg-delivery-rate-estimation-00):
https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00
<https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00>
我这里将给出一个tcptrace的图解,用图示的方式来解释:



有了这个图之后,我们就可以采用BBR动力学的方式来推算Startup gain了,毕竟整个BBR的运作全依仗着这个速率估算模型。

BBR Startup gain的推算

简单点讲,BBR的发送速率是通过采集到的上一个计时周期的传输速率乘以一个增益系数
计算出来的,在速率估算模型上,这个计时周期以连续收到的ACK时间点为间隔,而在我们的Startup
gain计算上,则将RTT作为间隔,这并不影响计算,因为我们本来就是要以RTT为单位计算增益系数的。

总之,不变的就是用既有的推算未知的。

好了,有了上述解释后,理解下面这个计算Startup gain的图示就简单多了:



我们一步一步把上图中的计算公式用数学符号写出来,就能导出Startup gain的值。为了书写和推导方便,我依然将该Startup gain写作GG。

先看slope2是什么。

很显然它就是我们需要拟合的已知曲线:

PacingRate=f(t)=2tPacingRate=f(t)=2t

我们需要的是,让真实的测量出来的PacingRatePacingRate和这条我们期望的已知曲线上的PacingRatePacingRate
相等即可,那么接下来,我们要看如何计算slope2。

slope2是一个斜率,代表测量出来的带宽,而BBR动力学模型里带宽的计算公式则是:

bw=data_ackedRTTbw=data_ackedRTT

我们已经设RTTRTT为1了,所以只需要计算图中的data_ackeddata_acked即可。总之,就是要计算数据包P1P1和P2P2
之间一共发送了多少数据包。这在物理上很容易表达。

我们已经知道速率随着RTTRTT的关系是PacingRate=f(t)PacingRate=f(t),请注意,这里的tt是以RTTRTT
为周期的,所以,这里需要计算的就是在上一个RTTRTT内一共发送了多少数据包,很显然,这里只是一个速率对RTTRTT的积分计算:

bw(t−1)=∫t−1t−2f(t)dtbw(t−1)=∫t−2t−1f(t)dt

根据BBR动力学模型,有以下的关系:

G×bw(t−1)=f(t)G×bw(t−1)=f(t)

所以,一路化简即可:

G×bw(t−1)=f(t)=G×∫t−1t−2f(t)dt=2tG×bw(t−1)=f(t)=G×∫t−2t−1f(t)dt=2t

G×2t4ln2=2tG×2t4ln⁡2=2t

所以:

G=4ln2G=4ln⁡2

如果采用从期望到测量的拟合方法,则有:



请注意,在这幅图中,后面的data_send就是实际中在一个RTTRTT内发送的数据包数量,因此它就是:

data_send(t−1)=∫tt−1f(t)dt=2t2ln2data_send(t−1)=∫t−1tf(t)dt=2t2ln⁡2

而此时,我们期望*这个积分是从一个已知的符合条件的f(t)=2t中演化而来的f(t)=2t中演化而来的,所以说,就有:

G×f(t−2)=data_send(t−1)G×f(t−2)=data_send(t−1)

化简就是:

G×2t−2=2t2ln2G×2t−2=2t2ln⁡2

结果:

G=2ln2G=2ln⁡2

好了,现在我们从两个方向推导出来两个GG,到底采用哪个呢?BBR的作者Neal告诉我需要用实际测试数据去修正模型。Neal告诉我BBR的团队目前正在G=4ln
2≈2.77G=4ln⁡2≈2.77这个模型上演进,并且给出了最新的进展,但是我仍然希望得到最初的G=2ln2≈2.89G=2ln⁡2≈2.89
这个模型的相关信息和推导,但是很遗憾:

But can you tell me the reason for the change in gain from 2/ln2 to 4ln2 and
the original derivation of 2/ln2.

The team member who did the original derivation that arrived at 2/ln(2)=2.89
did not have easy access to his notes from that original derivation (which was
several years ago at this point). So our team undertook to reconstruct the
derivation, and in this process we ended up with 4*ln(2)=2.77.

Note that the 2.77 gain is only 4% lower than the original 2.89. And the
original 2.89 is indeed sufficient to double the pacing rate each round trip.
It’s just that, in an idealized system, the 2.77 gain appears to be the
theoretical lower limit on a gain that can double the pacing rate each round
trip, when calculating the pacing rate as a multiple of the estimated bandwidth.

不过很不错,至少我们可以在纯数学的观感上
获得一个非常对称的模型,当然,最不错的是Neal总是有求必应,第一时间回复邮件,我想这是我们中国的工程师所要学习的,不管你牛不牛。

这里插几句题外话。
我见过很多牛逼哄哄的工程师,发邮件不回,发微信不回,我是真心求教的时候,往往发了很多疑问才会回复简单一句,显得他或者她很忙,很高端,显得没时间搭理我。

当然,没有人有义务回答别人的疑问,但并不说明屡次都可以这样。如果一直如此,我倒是觉得他们就像我们传统的官老爷一样,求之难,甚于求天。也许他们正是受到了中国传统官老爷文化的影响太深了。官老爷在西方文化里就像是小区里的物业和保安一样…不过即便是物业和保安,在我们这不也是天天牛气冲天么?

Startup阶段初始失速问题和优化

按照上文中的模型,理想情况下,每一个RTTRTT,数据包都在均匀发送和倍增:



是的,如果init_cwndinit_cwnd从1开始,Startup阶段,在RTTRTT固定的情况下,每一个RTTRTT
内数据包均能在这段时间内均匀分布,因为PacinRatePacinRate的计算也是均匀的。

但是,为了提高效率,经过测试,Google建议吧init_cwndinit_cwnd调整为10(测试数据是16,各种权衡,保守取10),这就会出现
cwnd,PacingRate和RTT三者之间不匹配的情形:



这种情形就是,如果PacingRatePacingRate
再慢一点,或者cwnd再大一点,就均匀了,然而在连接刚刚建立的时候,cwnd是预设的,而RTT大概率是拍出来的(受握手期间网络排队情况影响
),所以很难计算出一个合理的PacingRatePacingRate值。

所以在前几个RTTRTT
周期内,即便使用了Pacing,数据包的发送也会呈现断断续续而非平滑的情景,另外一个极端的情景就是大量丢包,这说明在PacingRate还没有测出来之前,cwnd设置大了。

所以说,我认为在Startup阶段前,还要有一个homogenize阶段,它的终点在所有的数据包在一个RTT内均匀发送
。具体实现就是另启一个定时器,超时时间为:

T=packets_total_lengthpacing_rate+αT=packets_total_lengthpacing_rate+α

理想不排队情况下,如果时间T内没有收到ack,就说明管道还没有均匀封闭,这个时候就可以再发送一个数据包并且重置该定时器。

这个算法可以让后续Delivery_rateDelivery_rate测量迅速均匀化。




很多人都简单地以为调大初始拥塞控制窗口是一件必做不可的事,但大多数人不知其所以然,特别是在Pacing起作用的情况下,调整初始窗口的影响就更加大了。如果一开始的计算做不到均匀化,那么后续的测量结果将会偏离期望值,系统的收敛时间也会同步增加。


如果你不懂这个值是怎么来的,就不要轻易去修改它,数值并不是越大越好,远远不是!为什么是20?为什么是18?为什么是60?…这个值如果不是你算出来的,就不要再误导他人了。

我讲一个故事。Google BBR团队丢失了2ln22ln⁡2的推导过程,所以他们重构了推导过程,导出了4ln2≈2.774ln⁡2≈2.77,
为什么不直接继续用2.892.89?因为不知道怎么来的!就这么简单。

为什么非要平滑?

因为要公平!

也许你会认为,激进一点不好吗?难道不是更快吗?这是大错特错!

Google的BBR团队一直致力于不排队,这点有目共睹,哪怕是单流性能损耗为代价,也在所不辞。TCP拥塞控制算法的目标在于最坏的情况下大家还有路可走,所谓
拥塞控制,正在此意。你如果非要想抢道,那便不是我所说的范畴。


那些盲改拥塞控制算法的伎俩都非常简单,增加初始拥塞窗口,reordering次重复ack后死活不降窗,加性增窗改成乘性增窗…不一而足,然而都是扯淡。在这条路上继续交流探索就会越走越远。

不考虑公平性的TCP拥塞控制算法,就是tmd玩笑!

浙江温州皮鞋湿

浙江温州皮鞋湿!