要点:一个方法无法精确刻画链路画像,就用多种方法组合刻画,设计一个 “自定义平均”。
当前 Linux kernel TCP 实现的 TCP delivery rate 测量机制(BBR 有使用到)如下图:
简略后可展示为下图:
详见 net/ipv4/tcp_rate.c(重点看 C 文件前面的注释,可省去看代码)。
这种方法有几个问题:
- 粒度为一个 round 滚动,在大 RTT 场景下无法观测到 round 内 RTT 细节。
- (如下图所示)很难捕捉流侵入瞬间的指标变化。
下图是一个良性流侵入前后的 case,流侵入后自觉排空 queue,RTT 重新收敛到传播时延,两条流均分带宽。但当前 Linux kernel TCP 的测量方法对待该 case 还是粒度过粗。
就更别提非良性流的侵入以及 incast 了。
在 漫谈拥塞控制: a Decade of Wasted Bandwidth? 一文中,我提到精确测量 delivery rate 的必要,要一个刻画 delivery rate 本身以及其变化的函数,而不仅是一种测量方法,换句话说,最终需要的是一幅链路画像。
上面提到的 Linux kernel 方法仅刻画一个 round 内的积累 delivery rate,记为 f(x),此处 x 为 kernel 函数 tcp_rate_gen 所需,除此之外还需要两类(暂时想到的,后续可能会更新)刻画手段一起做功。
刻画 round 内趋势的 g(x),如下图:
相当于在一个 or 几个 round 内做积累平均。
另外,还需要刻画 delivery date 瞬时变化的 h(x),如下图:
h(x) 体现的是 delivery rate or rtt 的梯度或者变化率。
最终的函数表达式如下:
T = alpha * f(x) + beta * g(x) + gamma * h(x)
其中 alpha + beta + gamma = 1 且 gamma > beta > alpha,gamma < alpha + beta。
f(x) 以 round 为粒度向前平滑滚动,g(x) 在一个或者几个 round 内平滑滚动,h(x) 间隔固定的足够小但又不至于样本不够的 delta(time) or delta(delivered) 捕捉细节,比如类似 bbr 那般 x rounds 的 win,三者合力刻画链路画像。
如果在稳定状态,f(x) = g(x) = h(x),T = f(x),与当前 Linux kernel 无异。
如果有侵入流变化导致 RTT 变化,h(x) 会迅速捕捉到,由于 gamma 比重大,h(x) 会迅速反应到 T 上。
如果 RTT 上下剧烈波动,由于 gamma < alpha + beta,f(x) 和 g(x) 会联合平滑 h(x),使 T 趋于平稳。
如果 RTT 在 round 内部发生持续变化且 RTT 较大,g(x) 会有所体现,且跨越 round 的变化又会被 f(x) 平滑。
…
总之,对结果做算术,不对过程做算术。
Linux kernel TCP 测量方式对测量过程做了 round 滚动平均的过程,这不合适,合适的做法是先把尽量采集精确样本,再采用算术方式计算结果。本文所示的 T 表达式只是我能想到的一种,换句话说,有没有能力处理样本是策略问题,能不能采集到精确样本是机制问题,机制先确定了,策略可以慢慢磨。
rate-based 算法主要目标是测量 delivery rate,识别拥塞并主导 pacing rate 而不是计算 cwnd,cwnd 仅作限制 in-flight,因此再精细的计算粒度也不足为过,也就不用纠结非整数的计算结果(比如 42.37 个 cwnd 的处理)了。
与传统 loss-based 算法不同,rate-based 算法实际的 delivery rate 测量结果无法作为一个边沿触发的 event,它更像一种水平触发的数据输出,而非一种状态。对 delivery rate 的反应是连续的过程而非一个固定的动作,如何设计这个连续的过程是 rate-based 算法的难点,好的算法应既能敏锐识别拥塞,又能过滤噪声,二者的矛盾本质意味着算法注定要在一个 buffer 上展开,为误判保留回退余地,BBR 状态机便是这样一个实例。
皮鞋没有蹬上,露着白袜子。
浙江温州皮鞋湿,下雨进水不会胖。