24、拥塞控制性能指标:吞吐量、延迟、抖动、公平性、收敛速度、评估方法
聊到拥塞控制,大家第一反应往往是算法本身——GCC、BBR、NADA,哪个更牛?但说实话,我在实际项目中踩过最大的坑,反而不是算法选型,而是不知道怎么衡量算法好不好。
你想想看,没有一把好用的尺子,你怎么知道调参是调对了还是调歪了?
所以这一章,咱们先把尺子定下来。我个人习惯把这六个指标称为「拥塞控制的六边形战士」:吞吐量、延迟、抖动、公平性、收敛速度,再加上评估方法。嗯,一个一个来拆。
吞吐量(Throughput)
吞吐量是最直观的指标。说白了,就是单位时间内能传多少数据。单位通常是 Mbps 或 Kbps。
但这里有个坑——吞吐量不等于带宽利用率。我曾经在某个项目中,看到吞吐量跑满了 10Mbps,但实际视频质量还是一塌糊涂。为什么?因为吞吐量高不代表数据都是有效的,重传、冗余包全算进去了。
吞吐量的测量方式也很简单:
// 伪代码:接收端统计吞吐量
uint64_t totalBytes = 0;
uint64_t startTime = now_ms();
// 每收到一个包就累加
onReceive(packet) {
totalBytes += packet.size;
}
// 每秒计算一次
everySecond() {
uint64_t elapsed = now_ms() - startTime;
double throughput = (totalBytes * 8) / (elapsed / 1000.0); // bps
reset();
}
延迟(Latency)
延迟是实时通信的命门。WebRTC 里我们通常关注两种延迟:
- 单向延迟(One-way Delay):从发送到接收的时间差
- 往返时间(RTT):发出去到收到确认的时间
我个人更看重单向延迟,因为 RTT 有时候会骗人。举个例子:如果网络是对称的,RTT/2 约等于单向延迟。但现实网络往往不对称——上行拥塞、下行通畅,这时候 RTT/2 就完全不准了。
抖动(Jitter)
抖动,说白了就是延迟的变化量。你想想看,如果延迟稳定在 50ms,哪怕绝对值高一点,接收端也能通过 jitter buffer 平滑处理。但如果延迟在 10ms 到 200ms 之间反复横跳,那视频就会一卡一卡的。
WebRTC 里常用的抖动计算公式是:
// 基于 RFC 3550 的抖动计算
double jitter = 0;
double previousJitter = 0;
onReceive(packet) {
double D = |(R_i - R_{i-1}) - (S_i - S_{i-1})|;
jitter = previousJitter + (|D| - previousJitter) / 16;
previousJitter = jitter;
}
这个公式有个特点:它用了一个 1/16 的平滑因子,不会因为单个异常包就剧烈抖动。嗯,这里要注意,如果网络突然变差,这个指标反应会慢半拍。我在项目中遇到过这种情况,后来加了一个「瞬时抖动」的辅助指标来弥补。
公平性(Fairness)
公平性是个很有意思的指标。它衡量的是:当多个流共享同一条链路时,每个流能不能分到「合理」的带宽。
常用的公平性指标是 Jain's Fairness Index:
| 公式 | 说明 |
|---|---|
| F = (∑x_i)² / (n * ∑x_i²) | x_i 是第 i 个流的吞吐量,n 是流数量 |
| F = 1 | 完全公平,所有流吞吐量相等 |
| F = 1/n | 最不公平,一个流占用了全部带宽 |
我记得有一次做多流测试,GCC 和 BBR 混跑,公平性指数只有 0.6 左右。BBR 把带宽抢走了大半,GCC 被压得死死的。后来我们调整了 BBR 的 pacing 参数,才把公平性拉到 0.85 以上。
收敛速度(Convergence Speed)
收敛速度衡量的是:当网络条件发生变化时,算法需要多久才能稳定到新的工作点。
举个例子:带宽从 5Mbps 突然降到 1Mbps,好的拥塞控制算法应该在 1-2 个 RTT 内就降速到位。如果花了 5 秒才反应过来,那这 5 秒里用户看到的全是卡顿和花屏。
我一般用两个指标来衡量收敛速度:
- 上升收敛时间:带宽增加时,吞吐量达到新带宽 90% 所需的时间
- 下降收敛时间:带宽减少时,吞吐量降到新带宽 110% 以内所需的时间
下降收敛时间比上升收敛时间更重要。为什么?因为带宽下降时如果不快速反应,就会造成大量丢包。我在项目中遇到过,某个算法上升收敛只要 200ms,但下降收敛要 2 秒——结果就是每次带宽抖动,视频就断流 2 秒。
评估方法
指标有了,怎么评估?我常用的方法有三种:
- 仿真测试:用 ns-3 或自定义的丢包/延迟模型,跑大量场景
- 回放测试:录制真实的网络 trace(比如 4G 网络下的带宽变化),回放给算法
- 实际网络测试:在真实互联网上跑,收集数据
我个人最推荐「回放测试」。因为仿真太理想化,实际网络又不可控。回放测试既能保证场景可复现,又能贴近真实网络的行为。
下面这张图是我常用的评估流程,你可以参考一下:
最后说一句,这六个指标不是孤立的。吞吐量和延迟之间就有天然的 trade-off——你追求极致吞吐量,延迟就会上升;你死磕低延迟,吞吐量就会下降。好的拥塞控制算法,就是在这些指标之间找到一个「甜点」。
嗯,这一章的内容就到这里。下一章我们会深入 GCC 算法的具体实现,看看 Google 是怎么在实时通信场景下平衡这些指标的。