开发文章

TCP拥塞控制算法纵横谈-BBR vs Reno/CUBIC

不管是在工作中,还是平时与技术上的朋友一起讨论关于BBR的问题,都不可避免的会面对“BBR和CUBIC之间的对比”的问题,毕竟CUBIC在Linux平台是默认的拥塞算法,已经好多年了。
        现在突然有了个被宣传的神乎其神的BBR算法,如果真的有那么好,那肯定要比CUBIC好,如果真的这样,到底好在哪里呢?如果不是这样,那么问题又是什么呢?
我想关于BBR好在哪里这个问题是不必回答的,因为如果在效率和公平性上秒杀了CUBIC,那基本上的结果就是换掉BBR,除了理论工作者没有人会问原因的。所以说,本文只是针对第二类问题展开讨论。

BBR&CUBIC时的动态过程分析

我先展示一个CUBIC流和BBR流的动态过程:
1.CUBIC与BBR共存于共享链路;
2.CUBIC会逐渐加大发包量,填充队列(其实,如果BBR完全按照临界点即最大带宽发送,CUBIC哪怕发一个包都会排队。。),这会造成BBR被动排队,BBR的表现就是:
1>.由于被动排队,rtt增加;
2>.由于有CUBIC共享带宽,检测到的带宽降低,这其实也受到1>的影响。

3.但是这在短时间内不会影响BBR的发包,因为BBR的测量带宽取的是一个窗口(默认10个RTT)内的最大带宽;
4.也就是说,只要这个带宽窗口以及最小RTT窗口(默认10秒)足以坚持到队列填满而不滑走,CUBIC一旦丢包就会乘性减窗,BBR趁机又可以恢复到干净状态,但是CUBIC还是会再重复之前的过程,我觉得整个过程CUBIC并不是受到了BBR的压制,这完全是一个正常行为,如果在CUBIC丢包时BBR的带宽窗口,RTT窗口(按照默认的10个rtt以及10s算)还没有滑走,只能说BBR恰好没有受到CUBIC的压制;
5.相反,CUBIC有时候会压制BBR,然而它(CUBIC)虽然获得了收益,但也必须付出代价;
6.整个过程中,BBR虽不会步步退让(这点由窗口内取值保证),但至少不会去主动排队,如果不是BBR跟CUBIC竞争,换成另一个cubic,是不是更糟糕呢?
7.另一种情况,如果队列足够深,rtt足够长,足以让CUBIC填充10s还不满时,CUBIC丢包前BBR就退到4个窗口(ProbeRTT)了,这反而说CUBIC压制了BBR;
8.结论是,浅队列时,CUBIC受BBR的影响比较小,深队列时,BBR会受到CUBIC的影响,此时BBR有点像ledbat了;
9.但是且慢,一切还要受AQM策略的影响!
10.没有结论,也说不准,但在大体情况下,BBR完胜。
...
可能我的分析有点绕,或者说比较垃圾,那么请看BBR作者的回答吧:
https://groups.google.com/forum/#!topic/bbr-dev/mvH4hGmc8dU
基本上都差不多,BBR比较简单好理解,所以不存在仁者见仁智者见智这个问题,所以答案几乎都一样,我也是在给朋友回复了上面的动态分析之后才看到这个论坛topic的,是在下班的班车上温州老板给我的链接。凡是觉得BBR比较棘手的,那几乎都是Google粉,相当大一部分是Google脑残粉,总之就是觉得Google的什么东西都是好的,涨Google志气,灭自家威风,其实说白了,这些脑残粉都是只破不立,就像方舟子一样,天天吆喝这也不对那也不对,自己却说不出半点对的,至少是建议也没有,这种负能量我天生反感。
        诚然,Google是一家伟大的公司,技术也很门槛很牛逼,但这并不意味只要是Google的东西都很门槛很牛逼,至少我认识的技术大牛里,有三四个都是在BBR之前就搞出了几乎一样的算法,比较早的是在2005年,我不知道他看到BBR是什么感觉,如果是我,我敢说当看到BBR时,绝对没有搞定网卡点灯那般兴奋。
        回到现实,继续扯技术。

可以忽略ProbeRTT阶段吗?

又是温州老板。温州老板看不惯ProbeRTT状态把窗口一下子降到4个MSS,所以与我之前的做法一样,老板忽略了ProbeRTT状态...
        好似一个美妙的“加速优化”。但是事实呢?我想温州老板也是照意念拍出来的方案,总之,心里有偏见,降窗无论如何都是不好的事,增窗无论如何都是好事。这种偏见影响了几乎所有的中国人,说这话并不是我在恶损中国人,而是因为我认识的外国人少,样本不足,不足以得出外国人的结论。
        好吧,只说一句,ProbeRTT的目标是重新探测最小的RTT。什么情况下需要这么做呢?很简单,在当前的RTT已经不可靠的情况下。10秒间,RTT不断增大,到底多少人在排队...BBR能保证的是,自己不排队!自己不排队的话就不会惩罚自己,然后别人的事就与自己无关了。这是根本!在这个根本之下,BBR出色的通过ProbeRTT搞定了公平性。那么,如何保证自己不排队呢?BBR给出的答案是保持4个MSS的窗口,那么,Why?

为什么是4个MSS

如果要想探测最小RTT,那么就“必须”不能排队,至少是必须不能主动排队,那么发出去一个MSS的数据段就是一个合理的动作,如果网络连一个段都承受不了而要排队的话,那显然是违背统计复用原则的。那么真的仅仅是只能发一个段吗?TCP段的发送到确认需要经过一个往返路径,考虑到TCP的单向ACK机制,在一个数据段在途未到达的同时,另一个已经被接收段的确认在返回的路径上也满足测量RTT时“仅有一个段在路径上”的假设,所以说,填满整个往返路径管道需要2个段。
        如果TCP的传输完全按照立即应答的原则来过的话,事情就会很简单,可惜TCP有个延迟应答机制,现在又有了LRO机制...这一切都是端到端控制与网络控制之间的矛盾所导致!这个矛盾其实也是流量控制和拥塞控制之间的矛盾。端到端是个人之间事,两方协商即可,而网络是一个共享平台,要多人博弈。举例来讲,你总是希望和与你交互的双方独占道路,可是另外一对交互者却不允许你们俩这么做...这便是网络的基本问题,这也是编程者程序员与网络设计人员考虑问题的不同出发点,所以说,你让编程者去设计一个网络协议,到头来一定很自私很垃圾。

为什么保持4个MSS就足够且恰好足够?

考虑最坏的情况,TCP数据接收端启用了Delay ACK,那么这意味着每收到2个MSS才能有一个ACK,这个ACK一下子确认两个以上的TCP段(考虑到ACK也可能丢失,所以用“以上”来形容),如果想保持ACK时钟的平滑运转,就必然将未被确认的数据段填满整个带宽时延乘积管道。在正向路径和反向路径(即ACK路径)都保有足够的数据段,其实就是2个MSS大小的数据段。
        不管怎样,这些数据都是inflight数据,何谓infilght?
        就是发出去未到达的,加上已经确认但确认还未到达的。这就是inflight。如果不排队,怎样才可以保持ACK时钟的畅通?很显然,inflight大于4个MSS段的数据即可,但是现在加了一个要求,自己主动不排队,这意味着自己只能发送4个MSS!至于其它的,与当前连接无关,取证即可。
        假设大家都开启Delay ACK,按照我的解释就是,2个段在路上,2个段的确认在路上,一共4个段,这就是最小的inflight!虽然有点相同,温州老板的解释是,一个将要发出的段在发送端,两个数据段已经被接收端确认,另外之前确认的数据段还在路上,温州皮鞋下雨进水不会胖。
...

Pacing Rate和拥塞窗口

有个争论是,到底是用窗口进行拥塞控制还是用速率进行拥塞控制。
        一直以来的方案是基于窗口进行拥塞控制,背后的想法是以网络上能承载的数据总量来控制拥塞,但是这个想法有一点不合理的地方,那就是网络管道是一个带宽时延乘积构成的二维管道,带有时间延展性质,网络承载的总量应该考虑时间因素,这些数据量应该均匀分布在整个管道中,而不仅仅只是一个数值。所以拥塞控制应该基于速率,而不是窗口。这种考虑是合理的,但窗口控制和速率控制并非二元论那样有我没他,它们事实上是胶着在一起的。基于窗口的控制事实上是将速率控制交给了网络。
        虽然数据的发送是突发的,然而如果网络中有调速装置的话,那么数据到达接收端便不是突发的,而是平缓到达的,进而ACK流也将是基于一定的速率平缓返回。我们知道,TCP数据的发送是ACK时钟流来驱动的,平缓的带有间隔的ACK到达事件必将驱动平缓的带有时间间隔的数据发送,结论就是,即便采用窗口控制法进行数据的突发,最终的发送行为也可能被ACK流调制成一定的速率...
        但是,以上成立的前提是窗口必须是准确的!事实上,窗口几乎从来没有准确过,所有的TCP拥塞控制算法都无法获得准确的窗口值,因此难免要基于AIMD模型来不断探测,这种模型带来的问题我就不多说了,Bufferbloat,是AIMD借助的方案也是AIMD带来的问题。解决之道就是采用测量探测模型替代AIMD探测模型,解决Bufferbloat问题,随之而来的就是采用速率控制代替窗口控制。
        测量模型解决Bufferbloat的思路很简单,如果我知道我最多只能按照10Mb/s的速率发送,我就不会提高到100Mb/s尝试多发,因为多发也没用。
        不管怎样,近些年来,越来越多的拥塞控制算法采用了速率控制,也就是说从数据的源头就缓解了突发,让数据一点一点地发送出去,量力而为,从而解决了Bufferbloat问题。BBR是采用测量模型的比较晚的算法了,其实早在Linux 3.10时代,TCP就已经导出了pacing rate这个变量了,不过说实话,BBR之前的pacing rate在解决Bufferbloat之外除了大多数情况下降低性能外几乎没有什么别的作用,原因在于其pacing rate就是简单地使用窗口除以RTT算出来的,这几乎是一个恒等式,因为网络管道本身就是如此定义的,即管道容量等于带宽与RTT的乘积。如果窗口是AIMD模型下得到的一个结论,那么pacing rate便也不是正确的。
        新一代的BBR算法采用了相对精确的测量得到了pacing rate值,为了平滑毛刺,其保存了一个容忍窗口(默认10个RTT)内的pacing rate取最大值。这好像已经完全靠谱了,但是这个时候窗口又蹦出来了。
        既然已经有了一个精确且合理的pacing rate,那为什么还要窗口呢?
        如果BBR一直保持匀速发送,且网络也允许它一直保持匀速发送,那么窗口确实是不必要的,然而BBR不可能一直匀速发送。网络管道是统计复用的,对于单独的一个TCP连接而言,其可用带宽是时刻在发生变化的,所以BBR不得不尝试去探测更多的带宽,同时当发现发送拥塞的时候,主动降速收敛。在ProbeMore阶段,BBR会尝试提高25%的发送速率,如果在一个RTT后采集到的反馈速率确实也增加了25%,说明这次探测是命中的,确实有新的空闲带宽,如果采集到的带宽无变化,那么说明此时并无新的空闲带宽腾出来。这就是ProbeMore的原理。
        现在的问题在于在ProbeMore阶段发送数据的总量应该是多少呢?
        很显然,如果已经没有空闲带宽,多发的数据肯定会丢失,如果有空闲带宽,也要等反馈回来确认了确实有空闲带宽后才能增加数据发送总量,所以说,必须保证在ProbeMore期间发送的inflight量不能增加。这就需要一个拥塞窗口来控制了。在已经完全理解BBR的前提下,我们设当前窗口内的最大带宽为W0,最小RTT为RTT0,那么以下的等式是确定的:
inflight0 = W0*RTT0
为了ProbeMore,BBR尝试增加25%的发送速率,如果探测成功确实有25%的空闲带宽,那么填满管道所需的inflight为:
inflight1 = (125%)*W0*RTT0
但是在未决之前,为了避免丢包,不能增加inflight量,所以说还是要保持inflight0,该值就是ProbeMore期间的拥塞窗口。如果过了一个RTT后,采集到了新的速率W1,那么:
W1 = 125%*W0
此时W1会替换W0成为新的最大带宽,同时RTT并未变化,仍未RTT0,这意味着网络可以容纳的inflight值为:
W1*RTT0 = (125%)*W0*RTT0 = inflight1
...

按照以上的分析,BBR会将当前的最大带宽和最小RTT的乘积作为当前的拥塞窗口(这里暂不考虑Delay ACK的影响)。在确认确实提速有效之前,BBR不会增加inflight的总量。

        总结下来就是BBR的ProbeMore分为两个阶段的不断循环:

第一阶段只提升发送速率并维持inflight
第二阶段确认第一阶段的提速并增加inflight

这个原则同时也说明了“测量即时速率”的必要性,BBR必须保证每收到一个ACK都能计算出一个即时速率,这样才可以确认或者否认在一个RTT前的ProbeMore提速措施是否有效,进而决定是否要增加拥塞窗口。
        这是与CUBIC完全不同的pacing rate与拥塞窗口之间的关系。
        虽然CUBIC也能根据窗口值算出一个pacing rate(按照上述推理,如果按照CUBIC的方式计算拥塞窗口,ProbeMore一开始就会将拥塞窗口增加25%,万一提速被否,将会丢失25%的数据),但是却跟BBR测量而得到的pacing rate拥有完全不同的意义。CUBIC的pacing rate与拥塞窗口是一个恒等式关系,背后就是BDP的定义,而BBR则是有“两阶段Probe”的深意在里面。
...
        其实也很好理解啊,速率是一个瞬时量,而inflight则是一个速率的积分,只要管道不满载,速率可以随时变换,inflight则是直接决定管道是否满载,且覆水难收。
        本节最后,引一段BBR论坛里的文字(来自:https://groups.google.com/forum/#!topic/bbr-dev/KHvgqYIl1cE),说的比较好:

In a standard TCP, where cwnd is the primary control and pacing has been added on top, that would be true.
BBR is different, because it uses pacing as the primary control.  A cwnd control is left in as a safety net, for cases where the pacing rate temporarily exceeds the delivery rate.  This is normal in BBR during bandwidth probes, and also occurs when the delivery rate reduces.

Broadly, having cwnd independent of rate*rtt allows BBR to tolerate much higher packet loss rates than conventional TCPs, without running out of window in which to perform loss recovery.  However, it does also result in filling the buffer when path contention increases, until BBR detects the contention and adjusts to  it.

 - Jonathan Morton

回到Reno/CUBIC

虽然本文以及自2016年10月份以来的文章都是在赞赏BBR的好,但回过头来发现Reno才是正真和谐的算法。不要拿Reno和BBR拼速度,而去比比适应性就知道了。
        要记住的是,TCP在设计之处并不针对任何实际链路,它本来就是一个普适的协议,其收敛模型不管是在56Kb带宽还是在1000Mb带宽,不管途径深队列,还是途径浅队列,都应该是完全一致的。如果画出其窗口/时间图,你会发现所有场景下的Reno算法都是相似形,按比例缩放而已,其背后竟然是一个超级简单的AIMD!
        然而,模型的和谐并不意味着其高度可用,虽然在1000Mb乃至10000Mb时的锯齿和56Kb时的锯齿形状上是一致的,但人们并不需要这种一致,相反,人们希望的是扭曲化的图形,按照时间轴扭曲这个窗口/时间图,于是便出现了很多的变种,比如BIC,CUBIC,Scalable,High Speed等,不管怎样,其背后都是Reno。
        Reno并非停滞了,依然在进化的道路上...
        就在2016年9月份,BBR半路杀出,似乎以压倒性的优势干翻了Reno一族...
        如果你相信中国崛起的话,我想这里面有点类似的东西。中国从先秦至19世纪初,一直是与西方持平(我比较客观,我不喜欢说中国古代秒杀西方之类)的,至少不会落后太多(在希罗时期,泛阿拉伯时期以及15世纪后在技术上比西方有所落后),然而19世纪中,西方便以压倒性的优势碾压中国,当时在进步思潮的影响下爆出的言论是中国的模式停滞了,这是个错误的模型,必须西化,采用西方的一切,包括文化!后来我们国内的很多学者也相信了这一套,从清末一直到文革,几乎都是以传统文化为耻的时代,这股风一直刮到了现在,影响了包括我自己在内的无数人。
        然而,我们看到,近些年西方那一套好像遇到了些问题,这恰恰是中国传统文化所能包容并解决的,似乎预示着某种意义上的回归...这里我就不展开说了。
        BBR碾压CUBIC,恰似鸦片战争,但Reno一族似乎依然在进化。

感谢 Bomb250 支持 磐实编程网 原文地址:
blog.csdn.net/dog250/article/details/62442522

文章信息

发布时间:2017-03-18

作者:Bomb250

发布者:aquwcw

浏览次数: