BBR 的 minRTT 采集问题
RTT 的增加并非一定意味着发生了拥塞,也可能是介质的问题甚至拓扑的问题,同理,RTT 的减少也并不意味着拥塞的缓解,做这个假设并基于这假设的一切行为都不明智。
前段时间在 BBR 讨论组看到一个有趣的事:bbr-dev] BBR v1 + XDP SYN Proxy in WAN has low throughput for 10s
显然,TCP 没有拓扑发现的能力(IPID 和 TTL 可以帮忙,但有限且 trick),导致这个问题很难有好的通用 fix 方案。说的其实就是我一直强调的 minrtt 随行的逻辑。由于 minrtt = RTprop + f(δ) 表示,就必须表示 f(δ),遗憾的是,这就好比企图在足球场上精确预测足球的轨迹一样难,能做的只是预测趋势。
意思就是,当基础 RTT 稳定变大的时候,minrtt 要跟着变大,基础 RTT 变小时,minrtt 要跟着变小,δ 抖动时,minrtt 保持不变。
再次强调,BBRv1 只是一个想法,一个新的尝试,它对网络的假设太理想,一旦偏离假设,行为也将不可控制。比如文初所列出的 issue,BBR 硬编码 10s 的 minrtt 窗口,一旦 minrtt 误采,10s 内将没有任何改出手段,即使基础 RTT 明显持续增大或定于新的但更大的稳定状态,BBR 也无法识别,这种状态下,它的 BDP 将被低估,从而进入 cwnd-limited 状态,最终影响吞吐。
我的做法是:
- 当 RTT 抖动越大,minrtt 定力越大;
- 当 RTT 持续下降,minrtt 收敛到稳定值,下降越快越稳定,收敛越快;
- 当 RTT 持续增加,minrtt 窗口缩短,增加越快越稳定,窗口过期越快,好重采集 minrtt 。
- …
我写了一个脚本来描述上面的话:
#!/bin/bashinput_file="sample.txt"
data_file="data.txt"awk '
function abs(x) {return (x < 0) ? -x : x;
}
function sigmoid(x) {# k = 0.317# x0 = 12k = 0.2;x0 = 15;return 1 / (1 + exp(-k * (x - x0)));
}BEGIN {alpha1 = 0.3;alpha2 = 0.3;beta = 0.7;ema1 = 0;ema2 = 0;len = 0;anch = 0;win = 0.8;avg_diff1 = 0;avg_diff2 = 0;
}
{value = $0;if (NR - 1 >= len) {anch = ema2 = ema1 = value;len = NR - 1 + 15;win = 0.4;} else {diff1 = abs(value - anch);diff1 = 0.6 * (diff1 ^ 2);diff2 = abs(value - ema2);diff2 = 0.6 * (diff2 ^ 2);avg_diff1 = (1 - beta) * avg_diff1 + beta * diff1;avg_diff2 = (1 - beta) * avg_diff2 + beta * diff2;alpha1 = sigmoid(avg_diff1);alpha2 = sigmoid(avg_diff2);win = 0.8;if (value > ema2) {len = len - 15 * alpha2;} else {ema1 = (1 - alpha1) * ema1 + alpha1 * value;ema2 = (1 - alpha2) * ema2 + alpha2 * value;len = NR - 1 + 15;win = 0.4;}}# 输出:行号, 原始值, ema1, ema2, win, lenprint NR, value, ema1, ema2, win, len;
}
' "$input_file" > "$data_file"gnuplot <<- EOFset terminal pngcairo enhanced font "Arial,12" size 1000,600set output "minrtt.png"set title "minrtt-(win-len & value)"set xlabel "t"set ylabel "value"set ytics nomirrorset y2ticsset gridset key top leftstats "$data_file" using 1 nooutputplot "$data_file" using 1:2 with linespoints lw 2 pt 7 title "即时样本", \"$data_file" every ::0::(STATS_records-2) using 1:4 with linespoints lw 2 dt 3 title "区间抖动均值", \"$data_file" every ::0::(STATS_records-2) using 1:3 with linespoints lw 2 dt 2 title "逐包抖动均值", \"$data_file" every ::0::(STATS_records-2) using 1:5 with lines lc "red" title "minrtt 窗口长度", \"$data_file" using (\$2 == 0 ? \$1 : NaN):5 with points pt 7 lc "red" notitle, \"$data_file" using (\$2 == 1 ? \$1 : NaN):5 with lines lc "red" lw 1 notitle
EOFecho "minrtt.png"
用下图紫色代表的一组样本数据来调参,获得下图绿色,蓝色两组数据(显然,代码的文字标识写反了,绿的应该是逐包抖动均值),最底下的红线,高凸起为 minrtt 窗口维持期,低凹陷是 minrtt 新样本采集点。最终,我们需要用蓝色线(区间抖动均值)来作为 minrtt 使用,绿色的逐包均值作为对比:
分别来看图中 1~8 的情况:
- 快速发现 RTT 上升并通过压缩窗口即时调整 minrtt 到稳定值附近;
- 快速收敛到稳定的 minrtt 即时稳定值;
- minrtt 虽跟随 RTT 上升(由于抖动),但又跟随它急剧下降;
- 同上;
- 快速发现 RTT 总体上升,局部抖动,收敛到稳定值;
- 快速发现 RTT 抖动,取均值;
- 过滤噪点,minrtt 不变;
- 同上(minrtt 虽上升一丢丢,但快速跟随真实稳定 minrtt 值);
- …
这实际上就是一种人工的智能调参,参数比较少,人工就够了(不得不再次提到我那内藏 1000 多个 if 分支的 C 函数),但若参数很多,就需要更多的样本集去训练了。
用我这个来替换 BBR 的 minrtt,minrtt 采小了无损吞吐,采大了无损公平。
浙江温州皮鞋湿,下雨进水不会胖。