為什么 BBRv2/3 測試下來吞吐遠不如 2016 年底的 BBRv1,這個事曾經提到過很多次,今天分析一下原理。注意三個事實:
- BBR 是一種擁塞控制算法;
- BBR 已經迭代到了 v3 版本;
- BBRv3 的 “性能” 遠不如 BBRv1.
第二點有點不可思議,一般而言,不存在版本越新性能越差的,如果有,一定是 bug,但對于 BBR 卻是不爭的事實。到底發生了什么?
如果你不知道擁塞控制算法的性能意味著什么,建議先去了解,這里不贅述。從一篇有趣的論文開始本文:BBR vs. BBRv2: A Performance Evaluation.
摘要部分最后幾句:
Our results suggest that BBRv2 trades performance for better fairness under losses. To bridge this gap, we investigate the workings of BBRv2 and find that BBRv2 employs a long-term upper bound on sending rate that is not robust to losses. This upper bound is continually decremented in the presence of persistent losses, thereby depressing goodput. We show that by aligning BBRv2’s upper bound with its maximum bandwidth estimation, BBRv2’s performance can be greatly improved while maintaining its fairness.
好家伙,還好只 aligning 了 upper bound,要是連 lower bound 也給 aligning 了,差一點就回退到 BBRv1 了。后面會分析到,upper bound 越大,吞吐越高,但必然以公平性為代價,若將 upper/lower bound 全取消,吞吐達到最優,公平性全無,因此這篇論文其實什么都沒說,就是滑動了一下滑桿,展示了這個交易。
BBRv2/3 增加這兩個 bound 的目標就是解決公平性問題,細節還得看 BBR Congestion Control(IETF-Draft):
BBR.inflight_hi(upper bound): The long-term maximum volume of in-flight data that the algorithm estimates will produce acceptable queue pressure, based on signals in the current or previous bandwidth probing cycle, as measured by loss. That is, if a flow is probing for bandwidth, and observes that sending a particular volume of in-flight data causes a loss rate higher than the loss rate objective, it sets inflight_hi to that volume of data. (Part of the long-term model.)
BBR.inflight_lo(lower bound): Analogous to BBR.bw_lo, the short-term maximum volume of in-flight data that the algorithm estimates is safe for matching the current network path delivery process, based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_inflight or inflight_hi (thus the name). (Part of the short-term model.)
BBRv2/3 遵循下列邏輯來調整 upper bound 和 lower bound:
// upper bound 在 ProbeUP & Refill phase 調整
loss_too_high_in_ProbeUP & Refill:inflight_hi = max_t(in_flight, bdp * (1 - beta);// lower bound 在 ProbeUP & Refill phase 之外調整
any_loss_except_ProbeUP & Refill:inflight_lo = max_t(max_inflight_latest, inflight_lo * (1 - beta);
用 (1 - beta) 兜底是為了 “ ensure that BBR does not react more dramatically than CUBIC’s 0.7x multiplicative decrease factor.”
long-term upper bound 維持 AIMD 周期間與 reno/cubic 的公平性,short-term lower bound 維持 AIMD 周期內與 reno/cubic 的公平性,如下圖所示:
就解釋一句,紅綠黃線下的面積是一個 cycle 內 BBRv2/3 的傳輸總量,它對應一個 AIMD 周期內虛線下面的面積,肉眼可見,大差不差,這就是所謂 “對 reno/cubic 的公平性”。
ProbeUP phase 是個受 loss_thresh 約束的指數增加 upper bound 的邏輯,期間,任何丟包率越過 loss_thresh 均將對 upper bound 進行 mutiplicatively decrease,整個 cycle 中只要發現比 upper bound 更大的實際 inflight,都會更新 inflight_hi,比如 ProbeUP 時的隨機丟包導致 loss_thresh 越界而 upper bound 被削減,后續的 cwnd_gain 將可能填充更大的 inflight。
注意,loss_thresh 越界時,只在 refill,ProbeUP phase 才更新 upper bound,因為 cruise 默認已在 drain phase 后完成適配 maxbw,minrtt 這個操作點了。
可見,BBRv2/3 除卻 BBR 狀態機就是一個 cwnd-based reno/cubic,因此有兩個結論:
- 在丟包率高于 loss_thresh 場景,BBRv2/3 維持與 reno/cubic 之公平性,吞吐跌落至與之持平;
- 在丟包率低于 loss_thresh 場景,BBRv2/3 獲得比 reno/cubic 更大的吞吐,提高了帶寬利用率。
雖說 BBRv2/3 采用了指數增加 upper bound 的方式,但為了和 reno/cubic 一致,BBR 確保在一個和 reno/cubic AIMD 周期一致的 cycle 內維持 upper bound 穩定(前面提到取實際 inflight 更新 inflight_hi 以對抗隨機丟包的技巧不在此約束內)而不再 ProbeUP,和 AIMD 零存整取不同,BBRv2/3 相當于整存整取。
BBRv2/3 PROBE_BW 的一個 cycle 確定為一個典型的 reno/cubic 流的 AIMD 周期,約 2s,或者動態設置為 BDP 個 round,因為一個 AIMD 周期就是 cwnd_max 個 round。
看這篇論文中展示 BBRv2 在丟包率 2% 下吞吐陡降的圖示:
2MB buffer,以 1KB mss 算,一共就是 2000 個段,按照 AIMD 理論,丟包率即 1/2000,相比 2% 的丟包率要低 40 倍,由 reno 吞吐公式可知其吞吐要高 根號 √40 倍,約等于 6.3,從圖上可見,inflight 理論上跌落到 5500*0.85/6.3 = 742,從圖上看,大差不差。
再看 BBRv1 論文中抗丟包的廣告圖(BBR 可容忍 20% 丟包,相比之下,cubic 連 1% 都容忍不了),若 long-term/short-term bound 均不受限(BBRv1 無 upper bound 約束),BBR 可支撐 20%+ 丟包率(解方程求出 gain = 1.25 時的最大丟包率),單獨 short-time bound 限制在均勻丟包的 reno/cubic 共存場景亦碾壓 reno/cubic,因此 BBRv2/3 的實際吞吐低的原因是其 long-term bound 即 upper bound 不斷被丟包壓制卻鮮有上升導致。
因此,想提高吞吐的方案就擺在眼前了:
- 要么升高 inflight_hi;
- 要么升高 loss_thresh。
但如論文所示,如此做法肯定損害了對 reno/cubic 的公平性,它的結論很明顯,如此修改對原生 BBRv2/3 而言提高了吞吐,而對 BBRv1 而言提高了公平性,好一個田忌賽馬。這個結論意味著,BBRv2/3 基本鎖死了吞吐和公平性的權衡,顧此失彼,不可兼得。
值得再次強調,當丟包率低于 loss_thresh = 2% 時,Probe_UP 不受阻礙,BBRv2/3 相對 reno/cubic 還是有很大吞吐優勢。BBRv2/3 與 reno/cubic 共存的宗旨就是,有難同當,有福獨享。
因此,BBRv2/3 吞吐不如 BBRv1 并非問題,而是特性,它并非論文指數的那樣,long-term bound inflight_hi 無法漲回去,雖然 short-term bound 確實會傳遞給 long-term bound,但正如其名字所表達,影響也只是 short-term,一旦丟包消除,long-term bound 下一個 cycle ProbeUP 時有自己的增長邏輯,即使在當前 cycle,由于 short-term bound inflight_lo 在 ProbeUP 和 Refill phase 不起作用,這兩個 phase 的 loss_thresh 越界雖 cut 了 inflight_hi,但若真的只是隨機丟包越界,在 cruise phase 靠 cwnd_gain 造成 inflight 超過 upper bound,仍可遞增 long-term bound。論文所下的結論,僅在其假設丟包率 2% 均勻分布(而不是隨機突發丟包)的場景有效。
若真要用通用方法提高吞吐,更好的做法是在 Cruise phase 只要不丟包就 additive increase upper bound,這最終就類似 BBR 旁路 cugas(cubic + vegas) 或 vebic(vegas + cubic) 了(or hybla?)。
如果想在模擬仿真環境(特別是 tc netem,mininet 這些玩意兒模擬一個固定的均勻丟包率)忽悠經理,讓經理看到吞吐碾壓原生算法的效果而獲得加薪晉升機會,我的方法就非常有用。因為絕大多數干這行的,證明給經理看或者寫論文的環境都是這類仿真環境,模擬均勻丟包,連 4-state 都不會(參見 揭露 BBR 的真相 最后部分)。
浙江溫州皮鞋濕,下雨進水不會胖。