如果 BBR 要跟 reno/cubic 公平,只能顾此失彼,没有任何变通方法,唯一的方法就是在放弃 reno/cubic,但前提你得保证 BBR 流之间是公平的。如果非要照顾 reno/cubic,那就必须要变成 reno/cubic,这就是 BBRv2/v3,但还是变得不够彻底,只能放弃,退而求 BBR 流自身之间的公平,本文就谈这个。而 BBR 自身的公平性问题,就是其 RTT 不公平。
节前最后工作日,一定要有解决这问题的思路才能吃顿烤鱼。
如何解决 BBRv2/v3 的 RTT 不公平问题,此前我一直的想法都是调整 gain,现在看来这是多么肤浅。真正的解法只要解除主要绳结,关注主要矛盾,别的次要问题自然而然化解。
可以明确,根据与 minrtt 负相关定性调整 gain 并不能获得定量公平,无非是让 RTT 小的流获得更大的 gain,而 RTT 大的流获得更小的 gain。具体来讲,可以一如既往地设计一个 sigmoid 函数,单调递减,定义域为 minrtt,取闭区间 [5ms, 100ms],值域为 gain,取闭区间 [1.18, 1.28],当然,还可以更简单地预处理查表,双斜率线性均可,唯一的要点是:
- RTT 越小,gain 增加的越慢,RTT 越大,gain 下降的越快,背后逻辑是 RTT 越大,BDP 矩越大,越容易产生队列,而 BBR 承诺抵抗队列。
但 gain 的范围如何界定?gain_max 和 gain_min 差异过小(如 [1.20, 1.25]),不足以展开收敛动态范围(没地方折腾就结束了),差异过大(如 [1.05, 1.50]),过大的 gain 会带来突发,产生队列,它甚至都不能缓解 RTT 不公平性问题,何谈解决。
OK,此路不通,那现在说高尚的定量做法。回想起 TCP Prague,就有了答案。
回到 BDP 矢量矩的概念,它是一个符合交换律的乘积,而 BBR 测量正交量种 maxbw 最为目标起作用,因此可将 gain 与 minrtt 结合重构 BDP 矢量矩:BDP = maxbw · (gain · minrtt),在流间公平时,maxbw 相等,gain · minrtt 亦要相等,因此根据 gain · minrtt 固定 gain 缩放 Probe 时间或者固定 minrtt 缩放 gain 就是我们直面选择的。
为达到目标,必须引入一个典型 minrtt 作为参照基准 M,比如若部署在广域网,即可引入国内典型 rtt 中位数,设 M = 25ms 做基准。
再次否定固定 minrtt 缩放 gain,因为若 1.25 · 25 = g · minrtt,g = 6.25 / minrtt。显然 g 会随着做分母的 minrtt 而大幅波动,g 作为 BDP 的倍数,一次性注入 pipe 的 inflight 的动作潜在助长了队列,而队列正是 BBR 所要极力消除的。gain = 1.25 附近刚刚好,永远不要试图通过大幅改变 gain 来优化算法行为,无论吞吐还是公平性。
所以必须走向另一种缩放方案,固定 gain 而缩放 Probe 时间。
现在的目标是让不同 minrtt 的流在 gain = 1.25 的柔缓均匀 pacing 下持续不同的时间,使它们 inflight 相等。同样基于 M 做缩放,由于量纲一致,它就是一个简单的线性缩放,在典型 minrtt = M 下,如果流 1 的 minrtt1 是流 2 的 minrtt2 的 m 倍,则它将持续流 2 时间的 1/m,设 probe持续时间为 x,等式是 1.25 · M · M = 1.25 · minrtt1 · x1 = 1.25 · minrtt2 · x2。
修改也简单,针对 BBRv2,也针对 v3,但 v3 修改要多一些:
c
bbr->probe_us = calc_probe_time(sk); // probe_us = 1.25 * M / bbr->min_rtt_us
...
if (bbr2_has_elapsed_in_phase(sk, bbr->probe_us) &&
...
is_bw_probe_done = true;
BBRv3 说是修正了 v2 在 ProbeUP 不能探到底的 bug,在 bbr_reset_full_bw(sk) 后重新探测,这导致了它的激进并加剧了 RTT 不公平,又是一起弄巧成拙。因此修正 v2 的手段不是重新探测到 full(探测到吃力),而是严格管理 ProbeUP 期间的 inflight,保持 inflight 公平,就什么都公平了。说白了就是保持 BDP 矩的公平,这是核心。
代码相关还有一点细节。ACK 时钟可能因为 gro,delayed,聚合等因素不稳定,所以借助本地时钟更好一些,代价就是 CPU。
说回正题。就整个算法而言,由于所有流都像 minrtt = M 的流对齐,那么对于 minrtt > M 的流而言,它们探测空闲带宽的速率将变慢,而对于 minrtt < M 的流而言,这个速度将变快。 交易必不可少。
一般情况下,将 M 做参数,在部署时设置是高尚的。在 DCN 场景,M = 50us,跨洋跨洲长传,M = 100ms,诸如此类。当然,一切还是要基于实际测量,通过统计你要部署网络现状的 RTT,绘制出 RTT 分布的 CDF,取斜率最大点做基础典型 RTT 方可定论。
在 ProbeUP pacing_rate = g 之外,Drain pacing_gain = 0.75 照章办事就行,因为 ProbeUP 已经对齐了,Drain phase 自然就已经齐了。核心问题只要确认都不难解决,但找到它很难,一旦核心问题解决,剩下的什么都不改,就自然解决了,否则就会越改越复杂。当事情变得越来越复杂,涉及越来越多时,就停手,大概率就是方向错了。
事实上,我最初仍想缩放 gain,但得知(从社会学)这多么不靠谱后,我发现自己根本就没有理解 BBR 最初的目标,我的魔改,以及其他几乎所有人的魔改行为,完全都跟我一样没有意识到 BBR 到底是什么,到底要解决什么问题。不管怎么说,总容量一定时,RTT 大的流危害大。反之相反。
浙江温州皮鞋湿,下雨进水不会胖。