10.2 CUBIC、BBR、pacing 与 active queue management
雨季让瓶颈链路前的队列越积越长,值守员想通过提高发送速率解决,结果延迟反而继续上升。
Congestion-control algorithm 决定 sender 如何根据 feedback 更新 sending model/window/rate。Queue discipline 决定 bottleneck 如何排队、mark 或 drop packet。Pacing 决定 sender 如何在时间上分布 packet。这三层可相互影响,不能用“换一个 TCP algorithm 就修复全网”概括。
1. CUBIC 仍是 loss-based controller,但 window growth 不再只按 ACK count 线性推进
RFC 9438 定义 CUBIC 的 standard form。Loss event 前的 window 记为 W_max,loss 后按 multiplicative factor 减小,随后 window target 主要根据自 congestion epoch 开始后的 elapsed time 在 W_max 附近走 cubic curve:
W_cubic(t) = C * (t - K)^3 + W_maxK 使 curve 在上次 W_max 附近有一段平缓区:
- 远低于
W_max时较快恢复; - 接近 old operating point 时谨慎 probe;
- 超过 old
W_max后继续探测新 capacity。
CUBIC 还有 Reno-friendly region、fast convergence、application-limited handling 与 overflow/precision requirement。一个只计 cubic formula并用 max(current, target) 的 Python 类不是 RFC-compliant CUBIC。
RFC 的 multiplicative-decrease factor 要按定义区分:beta_cubic 表示保留比例(标准推荐值为 0.7),不要一处把 beta=0.3 当“减少量”,另一处又用 cwnd *= beta。
CUBIC 减少 RTT bias,但不保证不同 RTT flow 绝对公平。ACK clock、loss probability、pacing、queue、Reno coexistence 和 implementation 仍影响 outcome。它在很多 Linux system 中常见,但“Linux 到今天在任意发行版都必然默认 CUBIC”不是可依赖 contract,应检查当前 host configuration。
2. BBR 建模 delivery rate 与 minimum RTT
BBR family 估计:
- Bottleneck bandwidth(BtlBw):recent delivery-rate sample 中对 bottleneck capacity 的 model;
- Round-trip propagation time(RTprop):一个 time window 内 minimum RTT 的 model,试图减少 queueing component。
Model 用于设 pacing rate 和 in-flight/cwnd target,并通过 gain cycling/probing 发现 bandwidth 变化。BtlBw * RTprop 提供 BDP estimate,但 BBR 不是简单固定在这个数值,它需要 ACK aggregation、policer、loss/ECN、startup/drain、probe scheduling 和 app-limited sample 处理。
“BBR 不看 loss”不准确。BBR 不把 first loss 当作和 Reno/CUBIC 一样的主要 operating point signal,但 implementation/version 会使用 loss、ECN 和 in-flight bound 处理 excessive congestion。BBR v1/v2/v3 的行为也不相同,不应用一个“每 8 RTT 先 1.25x 再 0.75x”的玩具类代表所有版本。
Model-based 不等于自动公平或无队列。BBR 与 loss-based flow 共享 drop-tail bottleneck、不同 RTT 或 policer 时的 fairness/queue behavior 需要实测,也是后续版本改进的重点。
3. Pacing 降低 burst,但不创造 capacity
Window controller 可允许一整个 ACK burst 的 data 立即发出。Pacing 将这些 packet 按 rate 分散到时间上,减少 microburst、queue spike 和 loss synchronization。
Pacing rate 设太高仍会排队,设太低会 underutilize。Timer granularity、NIC offload、qdisc 和 application burst 影响 packet 在 wire 上的真实间隔。TSO 使 kernel 向 NIC 交付 large segment 时,NIC 的 segmentation/pacing capability 决定它是否在 wire 上又变成 burst。
4. Bufferbloat 是过量 queueing delay,不只是“buffer 大”
Buffer 能 absorb short burst,但 persistently full queue 会把 RTT 从 propagation baseline 抬高到 queue drain time。Loss-based sender 如果要等 drop-tail queue 填满才收到 signal,latency-sensitive traffic 会在 throughput 仍很高时受到数百 ms 额外 delay。
Bufferbloat 应用 under-load latency 定义/观测,不只看 configured bytes。Link rate 降低时,同样 1 MB queue 的 drain time 会大幅增加,所以 variable-rate link(Wi-Fi/cellular)尤其难。
queueing delay ≈ queued bytes / bottleneck service rate这是一个量级估算,scheduling、packet size、wireless retransmission 和 rate change 会使 actual sojourn time 不同。
5. AQM 在 queue 溢出前 mark/drop
Active Queue Management(AQM)不等 buffer full 才反应,而是根据 queue state 提前 ECN-mark 或 drop,给 sender congestion signal。
CoDel 根据 packet sojourn time(在 queue 中停留多久)识别 persistent queue,而不只看 queue bytes。它有 target、interval、MTU/packet-mode 等 parameter/assumption,虽然目标是减少对 link bandwidth/RTT 的手工调参,但“CoDel 无任何配置参数”是错误的。
FQ-CoDel 先将 flow 分 queue/schedule,再对每个 queue 使用 CoDel-style AQM,防止一个 bulk flow 的 queue 完全压住 sparse latency-sensitive flow。Flow hashing 会 collision,non-responsive flow 与 tunnel aggregation 也需要考虑。CAKE 还组合 shaping、diffserv、host isolation 和 overhead compensation 等功能。
AQM 需要放在真 bottleneck queue 或通过 shaping 人为创建的可控 bottleneck。如果 ISP modem 的隐藏 queue 在你的 qdisc 之前就更慢,只在 LAN interface 换 qdisc 可能没有作用。
6. 观测比“切换算法”先一步
# 查 host 可用/默认 congestion-control algorithm
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control
# 查连接的 RTT、cwnd、retransmission、pacing/delivery-rate 等
ss -tin
# 查 queue discipline 和 drop/mark/backlog counter
tc -s qdisc showField 因 kernel/version/algorithm 而异。ss 看到 cwnd:100 时还要知道 MSS、bytes-in-flight、application-limited state、pacing rate、rwnd 和 RTT,不能用 cwnd 一个数字推算 throughput。
要比较 CUBIC/BBR/AQM,实验必须控制:
- bottleneck rate、base RTT、queue size/qdisc;
- loss/reordering/ECN policy;
- number of flows、flow start time、RTT asymmetry;
- sender/receiver kernel 与 offload;
- measurement duration 与 warm-up;
- throughput、p50/p95/p99 latency、loss/retransmission、fairness 与 CPU。
只测一次 file download 的完成时间,无法支撑“算法 A 比 B 快”的普遍结论。不要在 shared production host 直接全局切 sysctl 做试验;先用 network namespace/testbed 或 per-socket configuration,并准备 rollback。
7. 一个 BDP/queue-delay 量级计算器
def bdp_bytes(rate_mbps: float, base_rtt_ms: float) -> float:
if rate_mbps <= 0 or base_rtt_ms <= 0:
raise ValueError("rate and RTT must be positive")
return rate_mbps * 1_000_000 / 8 * base_rtt_ms / 1_000
def queue_delay_ms(queued_bytes: int, rate_mbps: float) -> float:
if queued_bytes < 0 or rate_mbps <= 0:
raise ValueError("invalid queue/rate")
return queued_bytes * 8 / (rate_mbps * 1_000_000) * 1_000
assert bdp_bytes(100, 20) == 250_000
assert queue_delay_ms(1_000_000, 20) == 400这只是 fluid-model estimate,不是对 packet scheduler 和 wireless link 的精确 simulator。它的用处是检查数量级:20 Mbps bottleneck 上堆 1 MB data 本身就需约 400 ms 才能 drain。
8. 验收问题
- CUBIC 的
W_max、K和 cubic epoch 分别帮助控制什么? - 为什么不能说 BBR “完全不看 loss”?
- Pacing 为什么能降低 burst,却不能创造 bottleneck capacity?
- CoDel 和 FQ-CoDel 有什么差异?
- 为什么 AQM 必须放在真正/可控的 bottleneck 才能管 queue delay?