Google's BBR擁塞控制算法模型解析

0.模型

模型是最根本的!算法

我很是討厭把全部的東西雜糅在一塊兒,我比較喜歡各個擊破,因此說,我最喜歡正交基!我但願把待觀測的東西分解成毫無耦合的N個方面,而後各自研究其特性。這個思路我曾經無數次提出,可是幾乎沒人會聽,由於一旦分解,你將看不到目標,看不到結果,拆了的東西並不定能再裝起來...使人欣慰的是,TCP的BBR算法思路也是這樣,不幸的是,TCP領域的頂級專家並無N維拆解,人家只是拆解了2個維度。緩存

帶寬和RTT BandWidth & RTT
我很驚奇Yuchung Cheng(鄭又中)和Neal Cardwell是怎麼發現這個正交基的,爲何以前30年都沒有人發現這個,最爲驚奇的是,他們居然對了!他們的模型基於下圖展開:網絡

 

 

這張圖幾乎徹底描述了網絡的行爲!這就是網絡傳輸的本質模型!之因此以前的Reno到CUBIC都是錯的,是由於它們沒有使用這個模型,我先來解釋一下這個模型,而後再看看將Reno/CUBIC套在這個模型上以後,是多麼的荒唐。工具

        值得注意的是,這個模型是Kleinrock & Gale早在1981年就提出來的,然而直到如今才被證實是有效的。以前的年月裏,人們面臨着實現問題(同時測量問題,後面會講)。所以我把這個模型最終的實現者做爲模型的主語,即Yuchung Cheng和Neal Cardwell們的模型,這並非說他們是模型的發明者。就相似牛頓的定律同樣,其實伽利略已經提出了,只是沒有用數學系統的論證並總結而已。性能

1.有破纔有立

首先,咱們先要知道Reno/CUBIC錯在哪裏,而後才能知道BBR是怎麼改進的。
        骨子裏,人們總但願時間成爲橫軸,除了生命和作愛,人類幾乎全部的行爲都是在於儘可能縮短期。
        效率是什麼?人們只知道效率的分母是時間!人們一旦把時間肯定爲橫軸,不少時候就會矇蔽不少真相。這是一個哲學問題,我之後再談。
        人們在爲TCP創建性能模型的時候,老是用」序列號-時間曲線「,」序列號RTT曲線「等來觀察(好比Wireshark等分析工具),目標呢,很簡單,就是查一下」一個鏈接最快能突突多少數據「,
        可是,這些都錯了!Why?
        由於時間軸具備滯後性欺騙特徵,全部基於時間軸的效率提高方案都是」先污染後治理「的方案!
        看一下上面那個模型圖,全部30年來的擁塞控制算法的目標都是收斂於一個錯誤的收斂點(圖的右邊):不斷地增長數據的傳輸,試圖填滿整個網絡以及網絡上的全部緩存,覺得這樣就會達到比較高的帶寬利用率,直到發現丟包,而後迅速下降數據發送量,以後從新向那個錯誤的收斂點前進,如此反覆。這就是鋸齒的根源!這個現象在以上的模型圖上顯示的很是明確,根本就不用解釋。若是一開始人們就使用這個模型圖,任何人都不由會問:爲何要一直在警惕區域徘徊呢?正確的收斂點不該該是暢通模式的最右端嗎??我以經典的VJ版TCP擁塞圖來講明一下:測試

 

 

咱們要作蜜蜂而不是老鼠。中北部省區的人們比較喜歡往家裏屯菜屯肉,最後總有大量的食物壞掉形成浪費,看似吞吐很高,實際效率極低,而南方人則不一樣,南方人歷來都是買當天正好夠吃的新鮮食物,毫不會往家裏屯。形成這種差別的緣由主要是由於北方冬天很是寒冷,食物難覓,而南方則一年四季都不愁新鮮食物。若是咱們深刻一下考慮這個問題,正確的作法必定是南方人的作法,任何北方人屯食物並非說他就喜歡屯食物,他知道屯的食物沒有新鮮的好,是出於沒有辦法才屯食物的,一旦條件成熟,好比大棚,溫室出現,北方人也開始購買當天的新鮮食物了。
        TCP與此不一樣,TCP的Reno/CUBIC並非由於條件不具有才往緩存裏屯數據包的,對於能搞出CUBIC那麼複雜算法的人,搞定BBR根本就是小菜一碟,我認可我看不太懂CUBIC的那些算式背後的東西,但我對BBR的理解很是清晰,像我這種半吊子水平幾個月前就差一點實現了BBR相似的東西,但我絕對想不到CUBIC那麼複雜的東西,之因此Reno/CUBIC被使用了30多年,徹底是由於人們一直覺得那是正確的作法,居然沒有任何人以爲這種作法是有問題的。
        基於{RTT,Delivery Rate}正交基的新模型一出來,人們好像猛然看到了事實的真相了:this

 


問題的根源在於,BBR擁塞控制模型和BBR以前的擁塞控制模型對BDP的定義不一樣。BBR的模型中,BDP是不包括網絡緩存的,而以前的模型中,是包括緩存的,這就是說,包括緩存的擁塞控制模型中,擁塞控制自己和網絡緩存之間有了強耦合!要想作一個好的擁塞控制算法,就必需要完全理解網絡緩存的行爲,然而做爲端到端的TCP協議,是不可能理解網絡緩存的。因此一直以來,30年以來都沒有出現一個比較好的算法,Reno到CUBIC,改進的只是算法自己,模型並無變!
        網絡緩存是複雜的,有基於深隊列的緩存,也有基於淺隊列的緩存,無論怎樣,都會遇到BufferBloat的問題,這是TCP所解決不了的,雖然如此,TCP仍是嘗試填滿包括這些永遠琢磨不透的網絡緩存在內的BDP,這個填滿的過程是逐步的,開始於慢啓動,而後...這個逐步填滿BDP是兩個過程,首先是RTT不變,逐漸填滿不包括網絡緩存在內的管道空間(在個人定義中,這個屬於時間延展性的緩存空間),而後是逐漸填滿網絡緩存的過程(在個人定義中,這部分屬於不帶時間延展性的時間牆空間),問題處在TCP對後一個過程的不可知不可測的特性!!
        既然知道了Reno/CUBIC的問題根源,那麼BBR是怎麼解決的呢?換句話說,BBR但願收斂在上圖中紅色圈圈的位置,它怎麼作到的呢?
        列舉作法以前,我先列舉一下{RTT,Delivery Rate}正交基的美妙:spa

 

 

誠然,BBR的模型已經發現了在一個足夠長但不太長的時間窗口內最大帶寬和最小RTT是其收斂點,這就使得BBR有了明確的目標,那就是,求出最大的帶寬,即Delivery Rate,以及求出最小的RTT,這個目標暫且擱置一下,先回答一個問題,爲何採樣時間窗口要足夠長,是爲了這段時間足以過濾掉假擁塞,畢竟時間能夠衝破一切謊話,沖淡一切悲哀,那爲何這段時間又不能過於長,由於要對網絡環境的變化有即時適應性。基於此,BBR能夠幾乎能夠抵禦大多數的假擁塞情形了。
        最終,BBR的BDP以下圖所示,再也不包括警惕區的網絡緩存:.net

 

 

我用一個統一的圖表示RTT和帶寬的關係:orm

 

 

好了,如今我看能夠看BBR到底怎麼在這個新模型下探測最大帶寬了。

2.BBR對最大帶寬和最小RTT的探測

從模型圖上能夠清楚的看出如何探測最大帶寬:

 

 

可是對於最小RTT的探測卻不是很直觀。
        在這裏首先要談一下BBR以前那些」基於時延「的擁塞控制算法。Reno/CUBIC屬於基於丟包的擁塞控制算法,而像Vegas之類的屬於基於時延的算法,其區別在於,Vegas對RTT的變化比較敏感,判斷擁塞的要素是RTT的變化而不是丟包,無論哪一種算法,都須要外部發生一個事件,提示TCP鏈接已經處於擁塞狀態了。事實證實,Vegas之類的算法工做的並很差,緣由在於它沒法抵抗假擁塞,偶爾一次非擁塞形成的RTT增長也會引起TCP主動降窗,這種算法沒法對抗共享深隊列的其它基於丟包的TCP鏈接的競爭,所以沒法廣泛被採用。
        BBR須要測量最小RTT,可是它是基於時延的擁塞算法嗎?
        並非!起初,當我第一次測BBR的時候,那是在國慶假期我醉酒後的次日,幾乎動用了家裏的全部設備(iMac一臺,Macbook Pro一臺,ThinkPad一臺,ROOT-Android平板一個,刷過的榮耀立方一個,樹莓派開發板一塊,ROOT-Android手機一個,極路由一個,iPhone兩個,iPad兩個...),搭建了一個測試網絡,讓BBR和CUBIC,Reno一塊兒跑,並製造了RTT突變,發現BBR並無降速,甚至對RTT的突變不會有反應,而對RTT的變化的劇烈反應是基於時延的擁塞算法的基本特徵,但BBR並無!任何組合狀況下BBR均可以完爆其它算法。這是爲何?
        BBR在一個不隨時間滑動的大概10秒的時間窗口中採集最小RTT,BBR只使用這個最小RTT計算Pacing Rate和擁塞窗口。BBR不會對RTT變大進行反應。可是若是整的發生了擁塞,RTT確實會變大,BBR怎麼發現這種狀況呢?答案就在於這個時間窗口的超期滑動,若是在一個時間窗口內持續沒有采集到更小的RTT,那麼就會將當前的RTT賦值個最小RTT。BBR就是這樣抵抗假擁塞的。秒級的窗口內,什麼都是瞞不住的。這就是RTT的測量以及使用原則:

 

 

如今能夠明確,BBR的搶佔性不糊由於其對時延的反應而下降,BBR不會對時延進行直接的反應。BBR沒有搶佔性,但也不示弱,它所作的是正確的作法所要求的,它只是作到了而已。
        以前在網上看到一個美國硅谷工做的實習生質疑BBR會由於時延反應而不利於公平性,我原本想回復的,後來感受翻次牆太麻煩了,

3.總結

咱們一直須要的是一個」可持續發展「的方案來解決效率問題。不幸的是,TCP出現的30年來,咱們見到的全部擁塞控制算法都是這種所謂的先污染後治理的方案,貫穿從Reno到CUBIC的全部算法。先把緩存填充到爆,而後再主動緩解,由於全部基於丟包的算法都在搶着填爆全部緩存,基於時延的算法太君子的主動降速行爲就顯得競爭性不夠,典型的劣幣驅良幣的場景。
        我一直都覺得TCP的加性增,乘性減是一我的人爲我,我爲人人的策略,確實,能夠這麼理解,然而這是一種敬人一杯,自罰一壺的策略,結局就是廣泛倒下...
        你們都能隨口說出ssthresh的做用是什麼,好比當窗口大於它就怎麼怎麼樣,當小於它就如何什麼的,可是有人知道它的含義嗎?也許,你會發現ss是slow start的縮寫,而thresh則是threshold的縮寫,門限的意思,可是這不是正確答案,正確的答案在這裏
The capacity of a path can be informally defined by the sum of unused available bandwidth in the forward path and the size of buffers at bottleneck routers.  
Typically, this capacity estimation is given by ssthresh.

真相如此晚近才被揭示,怪不得人們只知道ssthresh的用法而不知其含義啊!稍微詳細一點的東西也能夠看我寫的一篇文章《TCP核心概念-慢啓動,ssthresh,擁塞避免,公平性的真實含義》。
        事實上ssthresh定義了路徑上全部緩存(包括時間延展緩存和時間牆緩存),因此說,當檢測到丟包時,會將cwnd減小1/2並賦值給ssthresh,此時由於BDP已經滿載,因此說其容量的1/2就是路徑的緩存容量!Perfect!然而這是錯的,TCP誤覺得全部的緩存都是能夠填充的,然而事實倒是,只有時間延展性質的緩存,即網絡自己纔是能夠填充的,而時間牆緩存倒是應急的緩存,全部的TCP只要都避免使用時間牆緩存(包括路由器,交換機上的隊列緩存),其才能真正起到應急的做用,帶寬利用率纔會最大化!
        應急車道是應急的,而不是行車的!
        BBR的新模型把這一切錯誤展現給了全部人,所以,BBR的指示是,保持最大帶寬,並最小化網絡緩存的利用。事實上,基於新模型額BBR要比以前的算法簡單多了!千萬不要以爲新算法必定很難,相反,BBR超級簡單。

        也許你也已經想到了BBR相似的思路,可是它可以在Linux上實現仍是要對Linux的TCP實現動手術的,並不只僅是一個擁塞模塊那麼簡單,前幾天的文章說了N遍,BBR以前的擁塞控制算法在非Open狀態會被接管,再牛逼的算法也徹底沒用,因爲CUBIC試圖填滿整個包括隊列緩存在內的全部緩存空間,在當下的核心深隊列,邊緣淺隊列高速網絡環境中,只有不到40%的時間內TCP的擁塞狀態是處於Open狀態,大部分狀況,傳統的算法根本就跑不到!好在BBR的實現中,做者注意到了這一點,完成了TCP擁塞控制的外科手術,快哉!

        BBR會在4.9或者5.0內核中成爲默認的TCP擁塞控制算法嗎?我以爲可能還須要更多的測試,CUBIC雖然表現不佳,但起碼並無由於其表現帶來比較嚴重的問題,CUBIC的運行仍是很穩定的。可是我我的但願,我但願BBR趕忙成爲全部Linux版本的標配,完全結束所謂TCP單邊加速這個醜行!

附:時間延展性緩存和時間牆緩存

我一直強調BBR是簡單的,是由於它確實簡單。由於在上半年的時候,我差一點就想出了相似的算法,當時我已經準備對PRR作手術了。
        我以爲擁塞控制算法對擁塞窗口的控制權不夠大,我但願用擁塞算法自己的邏輯來絕對窗口如何調整,而不是一味地PRR!我不相信檢測到三次重複ACK就必定是發生了丟包,即使是在非Open狀態,好比Recovery狀態,我也但願在個人算法認爲能夠的狀況下能夠增長窗口...我仔細分析了網絡的特徵,總結除了兩類緩存:
帶有時間延展性的緩存,即網絡自己(確切的說是網線上跑的數據,從A到B須要時間,因此認爲網絡是具備存儲功能的); 時間牆緩存,即路由器交換機的隊列緩存。這類緩存的性質就是內存。

相關文章
相關標籤/搜索